├── dist ├── assets │ ├── app-theme-style.e3b0c442.css │ ├── AddJob.da20bbb4.css │ ├── index.802c9b48.css │ ├── header.d801b988.js │ ├── header.1b5fa5f8.jpg │ ├── logo.63028018.png │ ├── props.f48aca0b.js │ ├── SettingFooter.0be25544.css │ ├── SwitchItem.24688c5a.css │ ├── index.088ea227.css │ ├── SessionTimeoutLogin.f03ebbbe.css │ ├── siteSetting.c485f07c.js │ ├── SelectItem.3b3b269f.css │ ├── index.7b8b5e30.css │ ├── InputNumberItem.5677bd64.css │ ├── FrameBlank.cdeeabfc.js │ ├── useWindowSizeFn.48fcda03.js │ ├── index.05bbcef9.css │ ├── index.59e5fe5a.css │ ├── LockModal.0068f88c.css │ ├── index.6ce36d9e.css │ ├── index.4b6a1a28.js │ ├── index.0935299d.css │ ├── ThemeColorPicker.8eb61909.css │ ├── index.742490d2.js │ ├── SiderTrigger.9c9ec3fc.js │ ├── DetailModal.e31f13ac.js │ ├── download.29a0bc39.js │ ├── index.c483c751.js │ ├── index.e7586e6f.css │ ├── QuickNav.6011eb09.js │ ├── DropMenuItem.7d534f1b.js │ ├── Breadcrumb.45a81991.css │ ├── SiteAnalysis.60a5a3b3.js │ ├── data.2665c166.js │ ├── useContentViewHeight.4cbdde4a.js │ ├── useSortable.a6e1258f.js │ ├── ErrorAction.97ea071c.js │ ├── lock.0c0efc31.js │ ├── index.53ebad0e.js │ ├── DynamicInfo.49c499a3.js │ ├── TypePicker.9a3d6054.js │ ├── index.32fb0083.css │ ├── index.dfe67a3a.js │ ├── index.f8765bcc.js │ ├── index.a2505068.css │ ├── Exception.1646ace5.css │ ├── ProjectCard.34a3184e.js │ ├── SessionTimeoutLogin.bf633ee8.js │ ├── index.88b1d373.css │ ├── VisitAnalysisBar.5e71d8fc.js │ └── SalesProductPie.82502710.js ├── favicon.ico ├── resource │ ├── img │ │ ├── logo.png │ │ ├── pwa-192x192.png │ │ └── pwa-512x512.png │ └── tinymce │ │ └── skins │ │ └── ui │ │ ├── oxide │ │ ├── fonts │ │ │ └── tinymce-mobile.woff │ │ └── content.mobile.min.css │ │ └── oxide-dark │ │ └── content.mobile.min.css └── app.config.js ├── tests ├── __mocks__ │ ├── fileMock.ts │ ├── styleMock.ts │ └── workerMock.ts ├── server │ ├── nodemon.json │ ├── utils.ts │ ├── README.md │ ├── tsconfig.json │ ├── controller │ │ ├── UserController.ts │ │ └── FileController.ts │ ├── ecosystem.config.js │ ├── routes.ts │ ├── service │ │ └── UserService.ts │ └── package.json └── test.spec.ts ├── .stylelintignore ├── go.mod ├── src ├── views │ ├── sys │ │ ├── exception │ │ │ └── index.ts │ │ ├── iframe │ │ │ └── FrameBlank.vue │ │ ├── lock │ │ │ └── index.vue │ │ ├── redirect │ │ │ └── index.vue │ │ ├── error-log │ │ │ └── DetailModal.vue │ │ └── login │ │ │ └── LoginFormTitle.vue │ ├── bot │ │ ├── log │ │ │ └── index.vue │ │ └── test │ │ │ └── index.vue │ └── dashboard │ │ ├── analysis │ │ ├── components │ │ │ ├── props.ts │ │ │ └── SiteAnalysis.vue │ │ ├── data.ts │ │ └── index.vue │ │ └── workbench │ │ └── components │ │ ├── QuickNav.vue │ │ └── DynamicInfo.vue ├── design │ ├── config.less │ ├── transition │ │ ├── index.less │ │ ├── base.less │ │ ├── scale.less │ │ ├── zoom.less │ │ ├── slide.less │ │ └── scroll.less │ ├── ant │ │ └── input.less │ ├── var │ │ ├── index.less │ │ ├── easing.less │ │ └── breakpoint.less │ ├── index.less │ └── public.less ├── components │ ├── Menu │ │ ├── index.ts │ │ └── src │ │ │ ├── types.ts │ │ │ └── components │ │ │ ├── BasicMenuItem.vue │ │ │ └── MenuItemContent.vue │ ├── CodeEditor │ │ ├── src │ │ │ ├── typing.ts │ │ │ ├── json-preview │ │ │ │ └── JsonPreview.vue │ │ │ └── codemirror │ │ │ │ └── codeMirror.ts │ │ └── index.ts │ ├── Markdown │ │ ├── src │ │ │ ├── typing.ts │ │ │ └── MarkdownViewer.vue │ │ └── index.ts │ ├── Preview │ │ ├── index.ts │ │ └── src │ │ │ └── functional.ts │ ├── ContextMenu │ │ ├── index.ts │ │ └── src │ │ │ └── typing.ts │ ├── SimpleMenu │ │ ├── src │ │ │ ├── types.ts │ │ │ └── components │ │ │ │ ├── useSimpleMenuContext.ts │ │ │ │ └── types.ts │ │ └── index.ts │ ├── Time │ │ └── index.ts │ ├── CountTo │ │ └── index.ts │ ├── Qrcode │ │ ├── src │ │ │ ├── qrcodePlus.ts │ │ │ ├── toCanvas.ts │ │ │ └── typing.ts │ │ └── index.ts │ ├── CardList │ │ ├── index.ts │ │ └── src │ │ │ └── data.ts │ ├── Tinymce │ │ ├── index.ts │ │ └── src │ │ │ └── tinymce.ts │ ├── Authority │ │ └── index.ts │ ├── FlowChart │ │ ├── index.ts │ │ └── src │ │ │ ├── enum.ts │ │ │ ├── types.ts │ │ │ └── useFlowContext.ts │ ├── Form │ │ ├── src │ │ │ ├── types │ │ │ │ └── hooks.ts │ │ │ └── hooks │ │ │ │ ├── useComponentRegister.ts │ │ │ │ └── useFormContext.ts │ │ └── index.ts │ ├── Upload │ │ ├── index.ts │ │ └── src │ │ │ ├── ThumbUrl.vue │ │ │ └── helper.ts │ ├── VirtualScroll │ │ └── index.ts │ ├── ClickOutSide │ │ ├── index.ts │ │ └── src │ │ │ └── ClickOutSide.vue │ ├── StrengthMeter │ │ └── index.ts │ ├── Dropdown │ │ ├── index.ts │ │ └── src │ │ │ └── typing.ts │ ├── Loading │ │ ├── index.ts │ │ └── src │ │ │ └── typing.ts │ ├── Tree │ │ ├── index.ts │ │ └── src │ │ │ └── TreeIcon.ts │ ├── Cropper │ │ ├── src │ │ │ └── typing.ts │ │ └── index.ts │ ├── Scrollbar │ │ ├── index.ts │ │ └── src │ │ │ └── types.d.ts │ ├── Icon │ │ └── index.ts │ ├── Table │ │ ├── src │ │ │ ├── types │ │ │ │ ├── componentType.ts │ │ │ │ └── tableAction.ts │ │ │ ├── components │ │ │ │ ├── EditTableHeaderIcon.vue │ │ │ │ ├── ExpandIcon.tsx │ │ │ │ ├── editable │ │ │ │ │ └── helper.ts │ │ │ │ └── settings │ │ │ │ │ └── RedoSetting.vue │ │ │ ├── hooks │ │ │ │ ├── useLoading.ts │ │ │ │ ├── useTableContext.ts │ │ │ │ └── useTableStyle.ts │ │ │ ├── const.ts │ │ │ └── componentMap.ts │ │ └── index.ts │ ├── Description │ │ ├── index.ts │ │ └── src │ │ │ └── useDescription.ts │ ├── Drawer │ │ └── index.ts │ ├── CountDown │ │ └── index.ts │ ├── Application │ │ ├── src │ │ │ ├── search │ │ │ │ ├── AppSearchKeyItem.vue │ │ │ │ └── AppSearch.vue │ │ │ └── useAppContext.ts │ │ └── index.ts │ ├── Verify │ │ ├── src │ │ │ └── typing.ts │ │ └── index.ts │ ├── Modal │ │ ├── index.ts │ │ └── src │ │ │ ├── hooks │ │ │ └── useModalContext.ts │ │ │ └── components │ │ │ ├── ModalHeader.vue │ │ │ ├── Modal.tsx │ │ │ └── ModalFooter.vue │ ├── Page │ │ └── index.ts │ ├── Basic │ │ └── index.ts │ ├── Excel │ │ ├── index.ts │ │ └── src │ │ │ └── typing.ts │ ├── Button │ │ ├── index.ts │ │ └── src │ │ │ └── props.ts │ ├── registerGlobComp.ts │ ├── Container │ │ ├── index.ts │ │ └── src │ │ │ └── typing.ts │ └── Bot │ │ └── index.ts ├── assets │ ├── images │ │ ├── demo.png │ │ ├── logo.png │ │ └── header.jpg │ └── svg │ │ └── preview │ │ ├── resume.svg │ │ └── unscale.svg ├── locales │ └── lang │ │ ├── en │ │ ├── routes │ │ │ ├── basic.ts │ │ │ ├── dashboard.ts │ │ │ └── bot.ts │ │ └── common.ts │ │ ├── zh-CN │ │ ├── routes │ │ │ ├── basic.ts │ │ │ ├── dashboard.ts │ │ │ └── bot.ts │ │ └── common.ts │ │ ├── en.ts │ │ └── zh_CN.ts ├── api │ ├── sys │ │ ├── model │ │ │ ├── uploadModel.ts │ │ │ ├── menuModel.ts │ │ │ └── userModel.ts │ │ ├── menu.ts │ │ ├── upload.ts │ │ └── user.ts │ ├── model │ │ └── baseModel.ts │ ├── demo │ │ └── error.ts │ └── bot │ │ ├── job.ts │ │ └── model │ │ └── job.ts ├── enums │ ├── roleEnum.ts │ ├── pageEnum.ts │ ├── sizeEnum.ts │ ├── exceptionEnum.ts │ ├── breakpointEnum.ts │ ├── httpEnum.ts │ ├── cacheEnum.ts │ ├── menuEnum.ts │ └── appEnum.ts ├── store │ └── index.ts ├── logics │ ├── theme │ │ ├── updateGrayMode.ts │ │ ├── updateColorWeak.ts │ │ ├── util.ts │ │ ├── index.ts │ │ └── dark.ts │ └── mitt │ │ └── routeChange.ts ├── settings │ ├── siteSetting.ts │ ├── encryptionSetting.ts │ ├── localeSetting.ts │ └── designSetting.ts ├── utils │ ├── log.ts │ ├── socket │ │ └── websocket.ts │ ├── dateUtil.ts │ ├── uuid.ts │ ├── propTypes.ts │ ├── auth │ │ └── index.ts │ ├── helper │ │ └── tsxHelper.tsx │ ├── cq │ │ └── message.ts │ └── lib │ │ └── echarts.ts ├── hooks │ ├── web │ │ ├── useAppInject.ts │ │ ├── useContextMenu.ts │ │ ├── useSortable.ts │ │ ├── useDesign.ts │ │ ├── useFullContent.ts │ │ ├── usePagination.ts │ │ ├── useTitle.ts │ │ └── useScript.ts │ ├── core │ │ ├── onMountedOrActivated.ts │ │ ├── useRefs.ts │ │ ├── useLockFn.ts │ │ └── useTimeout.ts │ ├── component │ │ └── usePageContext.ts │ ├── setting │ │ ├── useMultipleTabSetting.ts │ │ ├── index.ts │ │ └── useTransitionSetting.ts │ └── event │ │ └── useWindowSizeFn.ts ├── directives │ ├── index.ts │ ├── ripple │ │ └── index.less │ ├── permission.ts │ └── repeatClick.ts ├── router │ ├── routes │ │ ├── mainOut.ts │ │ └── modules │ │ │ ├── about.ts │ │ │ └── dashboard.ts │ ├── constant.ts │ └── guard │ │ └── stateGuard.ts ├── layouts │ ├── default │ │ ├── header │ │ │ └── components │ │ │ │ ├── index.ts │ │ │ │ └── user-dropdown │ │ │ │ └── DropMenuItem.vue │ │ ├── tabs │ │ │ ├── types.ts │ │ │ └── components │ │ │ │ └── TabRedo.vue │ │ ├── setting │ │ │ ├── components │ │ │ │ └── index.ts │ │ │ └── index.vue │ │ ├── content │ │ │ └── useContentContext.ts │ │ └── trigger │ │ │ ├── SiderTrigger.vue │ │ │ ├── index.vue │ │ │ └── HeaderTrigger.vue │ ├── iframe │ │ └── index.vue │ └── page │ │ └── transition.ts └── App.vue ├── postcss.config.js ├── public ├── favicon.ico └── resource │ ├── img │ ├── logo.png │ ├── pwa-192x192.png │ └── pwa-512x512.png │ └── tinymce │ └── skins │ └── ui │ ├── oxide │ ├── fonts │ │ └── tinymce-mobile.woff │ └── content.mobile.min.css │ └── oxide-dark │ └── content.mobile.min.css ├── main.go ├── .gitpod.yml ├── .prettierignore ├── .husky ├── commit-msg ├── pre-commit ├── common.sh └── lintstagedrc.js ├── types ├── utils.d.ts ├── module.d.ts ├── index.d.ts └── store.d.ts ├── .env ├── .eslintignore ├── prettier.config.js ├── .vscode ├── launch.json └── extensions.json ├── .github ├── contributing.md ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── 3-bug-cn.md │ └── 2-feature.md └── workflows │ ├── issue-close-require.yml │ ├── release.yml │ └── issue-labeled.yml ├── .editorconfig ├── .gitignore ├── mock └── _createProductionServer.ts ├── .env.development ├── .yarnclean ├── commitlint.config.js ├── README.md ├── .env.test └── .env.production /dist/assets/app-theme-style.e3b0c442.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__mocks__/fileMock.ts: -------------------------------------------------------------------------------- 1 | export default ''; 2 | -------------------------------------------------------------------------------- /tests/__mocks__/styleMock.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | /public/* 3 | public/* 4 | -------------------------------------------------------------------------------- /dist/assets/AddJob.da20bbb4.css: -------------------------------------------------------------------------------- 1 | .vben-addjob__tip{white-space:pre-wrap} 2 | -------------------------------------------------------------------------------- /dist/assets/index.802c9b48.css: -------------------------------------------------------------------------------- 1 | .vben-request__action{margin:0 0 0 10px} 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/FloatTech/ZeroBot-Plugin-Webui 2 | 3 | go 1.20 4 | -------------------------------------------------------------------------------- /dist/assets/header.d801b988.js: -------------------------------------------------------------------------------- 1 | var a="/assets/header.1b5fa5f8.jpg";export{a as h}; 2 | -------------------------------------------------------------------------------- /src/views/sys/exception/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Exception } from './Exception.vue'; 2 | -------------------------------------------------------------------------------- /dist/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/favicon.ico -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/design/config.less: -------------------------------------------------------------------------------- 1 | @import (reference) 'color.less'; 2 | @import (reference) 'var/index.less'; 3 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package webui 2 | 3 | import ( 4 | "embed" 5 | ) 6 | 7 | //go:embed dist 8 | var Dist embed.FS -------------------------------------------------------------------------------- /src/components/Menu/index.ts: -------------------------------------------------------------------------------- 1 | import BasicMenu from './src/BasicMenu.vue'; 2 | 3 | export { BasicMenu }; 4 | -------------------------------------------------------------------------------- /dist/resource/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/resource/img/logo.png -------------------------------------------------------------------------------- /src/assets/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/src/assets/images/demo.png -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/locales/lang/en/routes/basic.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | login: 'Login', 3 | errorLogList: 'Error Log', 4 | }; 5 | -------------------------------------------------------------------------------- /src/locales/lang/zh-CN/routes/basic.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | login: '登录', 3 | errorLogList: '错误日志列表', 4 | }; 5 | -------------------------------------------------------------------------------- /public/resource/img/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/public/resource/img/logo.png -------------------------------------------------------------------------------- /src/assets/images/header.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/src/assets/images/header.jpg -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | ports: 2 | - port: 3344 3 | onOpen: open-preview 4 | tasks: 5 | - init: yarn 6 | command: yarn dev 7 | -------------------------------------------------------------------------------- /dist/assets/header.1b5fa5f8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/assets/header.1b5fa5f8.jpg -------------------------------------------------------------------------------- /dist/assets/logo.63028018.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/assets/logo.63028018.png -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /dist/* 2 | .local 3 | .output.js 4 | /node_modules/** 5 | 6 | **/*.svg 7 | **/*.sh 8 | 9 | /public/* 10 | -------------------------------------------------------------------------------- /dist/assets/props.f48aca0b.js: -------------------------------------------------------------------------------- 1 | const t={width:{type:String,default:"100%"},height:{type:String,default:"280px"}};export{t as b}; 2 | -------------------------------------------------------------------------------- /dist/resource/img/pwa-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/resource/img/pwa-192x192.png -------------------------------------------------------------------------------- /dist/resource/img/pwa-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/resource/img/pwa-512x512.png -------------------------------------------------------------------------------- /dist/assets/SettingFooter.0be25544.css: -------------------------------------------------------------------------------- 1 | .vben-setting-footer[data-v-40ca4b3c]{display:flex;flex-direction:column;align-items:center} 2 | -------------------------------------------------------------------------------- /public/resource/img/pwa-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/public/resource/img/pwa-192x192.png -------------------------------------------------------------------------------- /public/resource/img/pwa-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/public/resource/img/pwa-512x512.png -------------------------------------------------------------------------------- /dist/assets/SwitchItem.24688c5a.css: -------------------------------------------------------------------------------- 1 | .vben-setting-switch-item[data-v-fd7354e2]{display:flex;justify-content:space-between;margin:16px 0} 2 | -------------------------------------------------------------------------------- /src/api/sys/model/uploadModel.ts: -------------------------------------------------------------------------------- 1 | export interface UploadApiResult { 2 | message: string; 3 | code: number; 4 | url: string; 5 | } 6 | -------------------------------------------------------------------------------- /src/enums/roleEnum.ts: -------------------------------------------------------------------------------- 1 | export enum RoleEnum { 2 | // super admin 3 | SUPER = 'super', 4 | 5 | // tester 6 | TEST = 'test', 7 | } 8 | -------------------------------------------------------------------------------- /dist/assets/index.088ea227.css: -------------------------------------------------------------------------------- 1 | .vben-job__action{margin:0 0 0 10px}.vben-job__add{margin:10px 0 0 10px}.vben-job__clear{margin:10px 0 10px 10px} 2 | -------------------------------------------------------------------------------- /dist/assets/SessionTimeoutLogin.f03ebbbe.css: -------------------------------------------------------------------------------- 1 | .vben-st-login[data-v-6fac6116]{position:fixed;z-index:9999999;width:100%;height:100%;background:#fff} 2 | -------------------------------------------------------------------------------- /src/components/CodeEditor/src/typing.ts: -------------------------------------------------------------------------------- 1 | export enum MODE { 2 | JSON = 'application/json', 3 | HTML = 'htmlmixed', 4 | JS = 'javascript', 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Markdown/src/typing.ts: -------------------------------------------------------------------------------- 1 | import Vditor from 'vditor'; 2 | export interface MarkDownActionType { 3 | getVditor: () => Vditor; 4 | } 5 | -------------------------------------------------------------------------------- /src/components/Preview/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ImagePreview } from './src/Preview.vue'; 2 | export { createImgPreview } from './src/functional'; 3 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # shellcheck source=./_/husky.sh 4 | . "$(dirname "$0")/_/husky.sh" 5 | 6 | npx --no-install commitlint --edit "$1" 7 | -------------------------------------------------------------------------------- /src/components/ContextMenu/index.ts: -------------------------------------------------------------------------------- 1 | export { createContextMenu, destroyContextMenu } from './src/createContextMenu'; 2 | 3 | export * from './src/typing'; 4 | -------------------------------------------------------------------------------- /src/components/SimpleMenu/src/types.ts: -------------------------------------------------------------------------------- 1 | export interface MenuState { 2 | activeName: string; 3 | openNames: string[]; 4 | activeSubMenuNames: string[]; 5 | } 6 | -------------------------------------------------------------------------------- /src/components/Time/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils/index'; 2 | import time from './src/Time.vue'; 3 | 4 | export const Time = withInstall(time); 5 | -------------------------------------------------------------------------------- /src/components/SimpleMenu/index.ts: -------------------------------------------------------------------------------- 1 | export { default as SimpleMenu } from './src/SimpleMenu.vue'; 2 | export { default as SimpleMenuTag } from './src/SimpleMenuTag.vue'; 3 | -------------------------------------------------------------------------------- /types/utils.d.ts: -------------------------------------------------------------------------------- 1 | import type { ComputedRef, Ref } from 'vue'; 2 | 3 | export type DynamicProps = { 4 | [P in keyof T]: Ref | T[P] | ComputedRef; 5 | }; 6 | -------------------------------------------------------------------------------- /dist/assets/siteSetting.c485f07c.js: -------------------------------------------------------------------------------- 1 | const n="https://github.com/anncwb/vue-vben-admin",t="https://vvbin.cn/doc-next/",s="https://vvbin.cn/next/";export{t as D,n as G,s as S}; 2 | -------------------------------------------------------------------------------- /src/components/CountTo/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import countTo from './src/CountTo.vue'; 3 | 4 | export const CountTo = withInstall(countTo); 5 | -------------------------------------------------------------------------------- /src/components/Qrcode/src/qrcodePlus.ts: -------------------------------------------------------------------------------- 1 | // 参考 qr-code-with-logo 进行ts版本修改 2 | import { toCanvas } from './toCanvas'; 3 | export * from './typing'; 4 | export { toCanvas }; 5 | -------------------------------------------------------------------------------- /src/locales/lang/en/routes/dashboard.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: 'Dashboard', 3 | about: 'About', 4 | workbench: 'Workbench', 5 | analysis: 'Analysis', 6 | }; 7 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # port 2 | VITE_PORT = 3100 3 | 4 | # spa-title 5 | VITE_GLOB_APP_TITLE = ZeroBot_Plugin_Webui 6 | 7 | # spa shortname 8 | VITE_GLOB_APP_SHORT_NAME = ZeroBot_Plugin_Webui 9 | -------------------------------------------------------------------------------- /src/components/CardList/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import cardList from './src/CardList.vue'; 3 | 4 | export const CardList = withInstall(cardList); 5 | -------------------------------------------------------------------------------- /src/components/Tinymce/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils/index'; 2 | import tinymce from './src/Editor.vue'; 3 | 4 | export const Tinymce = withInstall(tinymce); 5 | -------------------------------------------------------------------------------- /tests/__mocks__/workerMock.ts: -------------------------------------------------------------------------------- 1 | export default jest.fn().mockImplementation(() => ({ 2 | postMessage: jest.fn(), 3 | onmessage: jest.fn(), 4 | onerror: jest.fn(), 5 | })); 6 | -------------------------------------------------------------------------------- /src/components/Authority/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import authority from './src/Authority.vue'; 3 | 4 | export const Authority = withInstall(authority); 5 | -------------------------------------------------------------------------------- /src/components/FlowChart/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import flowChart from './src/FlowChart.vue'; 3 | 4 | export const FlowChart = withInstall(flowChart); 5 | -------------------------------------------------------------------------------- /src/components/Form/src/types/hooks.ts: -------------------------------------------------------------------------------- 1 | export interface AdvanceState { 2 | isAdvanced: boolean; 3 | hideAdvanceBtn: boolean; 4 | isLoad: boolean; 5 | actionSpan: number; 6 | } 7 | -------------------------------------------------------------------------------- /src/locales/lang/zh-CN/routes/dashboard.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | dashboard: 'Dashboard', 3 | about: '关于', 4 | bot: '机器人客户端', 5 | workbench: '工作台', 6 | analysis: '分析页', 7 | }; 8 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | 2 | *.sh 3 | node_modules 4 | *.md 5 | *.woff 6 | *.ttf 7 | .vscode 8 | .idea 9 | dist 10 | /public 11 | /docs 12 | .husky 13 | .local 14 | /bin 15 | Dockerfile 16 | -------------------------------------------------------------------------------- /dist/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/dist/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff -------------------------------------------------------------------------------- /src/components/Upload/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import basicUpload from './src/BasicUpload.vue'; 3 | 4 | export const BasicUpload = withInstall(basicUpload); 5 | -------------------------------------------------------------------------------- /src/components/VirtualScroll/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils/index'; 2 | import vScroll from './src/VirtualScroll.vue'; 3 | 4 | export const VScroll = withInstall(vScroll); 5 | -------------------------------------------------------------------------------- /public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/FloatTech/ZeroBot-Plugin-Webui/HEAD/public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff -------------------------------------------------------------------------------- /tests/server/nodemon.json: -------------------------------------------------------------------------------- 1 | { 2 | "watch": ["src"], 3 | "ext": "ts", 4 | "exec": "ts-node -r tsconfig-paths/register index.ts", 5 | "events": { 6 | "restart": "clear" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /dist/assets/SelectItem.3b3b269f.css: -------------------------------------------------------------------------------- 1 | .vben-setting-select-item[data-v-2dba4940]{display:flex;justify-content:space-between;margin:16px 0}.vben-setting-select-item-select[data-v-2dba4940]{width:126px} 2 | -------------------------------------------------------------------------------- /src/components/ClickOutSide/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import clickOutSide from './src/ClickOutSide.vue'; 3 | 4 | export const ClickOutSide = withInstall(clickOutSide); 5 | -------------------------------------------------------------------------------- /src/components/Qrcode/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import qrCode from './src/Qrcode.vue'; 3 | 4 | export const QrCode = withInstall(qrCode); 5 | export * from './src/typing'; 6 | -------------------------------------------------------------------------------- /tests/server/utils.ts: -------------------------------------------------------------------------------- 1 | export class Result { 2 | static success(data: any) { 3 | return { 4 | code: 0, 5 | success: true, 6 | result: data, 7 | }; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /dist/assets/index.7b8b5e30.css: -------------------------------------------------------------------------------- 1 | .vben-countdown-input .ant-input-group-addon{padding-right:0;background-color:transparent;border:none}.vben-countdown-input .ant-input-group-addon button{font-size:14px} 2 | -------------------------------------------------------------------------------- /src/components/StrengthMeter/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import strengthMeter from './src/StrengthMeter.vue'; 3 | 4 | export const StrengthMeter = withInstall(strengthMeter); 5 | -------------------------------------------------------------------------------- /src/components/Dropdown/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import dropdown from './src/Dropdown.vue'; 3 | 4 | export * from './src/typing'; 5 | export const Dropdown = withInstall(dropdown); 6 | -------------------------------------------------------------------------------- /src/components/Loading/index.ts: -------------------------------------------------------------------------------- 1 | import Loading from './src/Loading.vue'; 2 | 3 | export { Loading }; 4 | export { useLoading } from './src/useLoading'; 5 | export { createLoading } from './src/createLoading'; 6 | -------------------------------------------------------------------------------- /src/components/Tree/index.ts: -------------------------------------------------------------------------------- 1 | import BasicTree from './src/Tree.vue'; 2 | 3 | export { BasicTree }; 4 | export type { ContextMenuItem } from '/@/hooks/web/useContextMenu'; 5 | export * from './src/typing'; 6 | -------------------------------------------------------------------------------- /src/api/model/baseModel.ts: -------------------------------------------------------------------------------- 1 | export interface BasicPageParams { 2 | page: number; 3 | pageSize: number; 4 | } 5 | 6 | export interface BasicFetchResult { 7 | items: T[]; 8 | total: number; 9 | } 10 | -------------------------------------------------------------------------------- /src/components/Cropper/src/typing.ts: -------------------------------------------------------------------------------- 1 | import type Cropper from 'cropperjs'; 2 | 3 | export interface CropendResult { 4 | imgBase64: string; 5 | imgInfo: Cropper.Data; 6 | } 7 | 8 | export type { Cropper }; 9 | -------------------------------------------------------------------------------- /src/components/Scrollbar/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * copy from element-ui 3 | */ 4 | 5 | import Scrollbar from './src/Scrollbar.vue'; 6 | 7 | export { Scrollbar }; 8 | export type { ScrollbarType } from './src/types'; 9 | -------------------------------------------------------------------------------- /src/locales/lang/zh-CN/routes/bot.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | bot: '机器人客户端', 3 | plugin: '插件管理', 4 | log: '日志管理', 5 | msg: '消息管理', 6 | test: '测试', 7 | request: '请求管理', 8 | job: '任务管理', 9 | }; 10 | -------------------------------------------------------------------------------- /dist/assets/InputNumberItem.5677bd64.css: -------------------------------------------------------------------------------- 1 | .vben-setting-input-number-item[data-v-19883dde]{display:flex;justify-content:space-between;margin:16px 0}.vben-setting-input-number-item-input-number[data-v-19883dde]{width:126px} 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | . "$(dirname "$0")/common.sh" 4 | 5 | [ -n "$CI" ] && exit 0 6 | 7 | # Format and submit code according to lintstagedrc.js configuration 8 | npm run lint:lint-staged 9 | -------------------------------------------------------------------------------- /.husky/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | command_exists () { 3 | command -v "$1" >/dev/null 2>&1 4 | } 5 | 6 | # Workaround for Windows 10, Git Bash and Yarn 7 | if command_exists winpty && test -t 1; then 8 | exec < /dev/tty 9 | fi 10 | -------------------------------------------------------------------------------- /src/components/Icon/index.ts: -------------------------------------------------------------------------------- 1 | import Icon from './src/Icon.vue'; 2 | import SvgIcon from './src/SvgIcon.vue'; 3 | import IconPicker from './src/IconPicker.vue'; 4 | 5 | export { Icon, IconPicker, SvgIcon }; 6 | 7 | export default Icon; 8 | -------------------------------------------------------------------------------- /src/components/Dropdown/src/typing.ts: -------------------------------------------------------------------------------- 1 | export interface DropMenu { 2 | onClick?: Fn; 3 | to?: string; 4 | icon?: string; 5 | event: string | number; 6 | text: string; 7 | disabled?: boolean; 8 | divider?: boolean; 9 | } 10 | -------------------------------------------------------------------------------- /src/views/sys/iframe/FrameBlank.vue: -------------------------------------------------------------------------------- 1 | 4 | 10 | -------------------------------------------------------------------------------- /dist/assets/FrameBlank.cdeeabfc.js: -------------------------------------------------------------------------------- 1 | import{_ as e}from"./index.c19540a9.js";import{A as a,B as n,D as r}from"./vendor.a45ea33e.js";const o=a({name:"FrameBlank"});function t(s,c,m,p,_,d){return n(),r("div")}var l=e(o,[["render",t]]);export{l as default}; 2 | -------------------------------------------------------------------------------- /src/store/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { createPinia } from 'pinia'; 3 | 4 | const store = createPinia(); 5 | 6 | export function setupStore(app: App) { 7 | app.use(store); 8 | } 9 | 10 | export { store }; 11 | -------------------------------------------------------------------------------- /src/components/Table/src/types/componentType.ts: -------------------------------------------------------------------------------- 1 | export type ComponentType = 2 | | 'Input' 3 | | 'InputNumber' 4 | | 'Select' 5 | | 'ApiSelect' 6 | | 'ApiTreeSelect' 7 | | 'Checkbox' 8 | | 'Switch' 9 | | 'DatePicker' 10 | | 'TimePicker'; 11 | -------------------------------------------------------------------------------- /src/locales/lang/en/routes/bot.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | bot: 'bot client', 3 | plugin: 'plugin management', 4 | log: 'log management', 5 | msg: 'message management', 6 | test: 'test', 7 | request: 'request management', 8 | job: 'job management', 9 | }; 10 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 100, 3 | semi: true, 4 | vueIndentScriptAndStyle: true, 5 | singleQuote: true, 6 | trailingComma: 'all', 7 | proseWrap: 'never', 8 | htmlWhitespaceSensitivity: 'strict', 9 | endOfLine: 'auto', 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/Description/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import description from './src/Description.vue'; 3 | 4 | export * from './src/typing'; 5 | export { useDescription } from './src/useDescription'; 6 | export const Description = withInstall(description); 7 | -------------------------------------------------------------------------------- /src/components/Drawer/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import basicDrawer from './src/BasicDrawer.vue'; 3 | 4 | export const BasicDrawer = withInstall(basicDrawer); 5 | export * from './src/typing'; 6 | export { useDrawer, useDrawerInner } from './src/useDrawer'; 7 | -------------------------------------------------------------------------------- /src/components/FlowChart/src/enum.ts: -------------------------------------------------------------------------------- 1 | export enum ToolbarTypeEnum { 2 | ZOOM_IN = 'zoomIn', 3 | ZOOM_OUT = 'zoomOut', 4 | RESET_ZOOM = 'resetZoom', 5 | 6 | UNDO = 'undo', 7 | REDO = 'redo', 8 | 9 | SNAPSHOT = 'snapshot', 10 | VIEW_DATA = 'viewData', 11 | } 12 | -------------------------------------------------------------------------------- /src/logics/theme/updateGrayMode.ts: -------------------------------------------------------------------------------- 1 | import { toggleClass } from './util'; 2 | 3 | /** 4 | * Change project gray mode status 5 | * @param gray 6 | */ 7 | export function updateGrayMode(gray: boolean) { 8 | toggleClass(gray, 'gray-mode', document.documentElement); 9 | } 10 | -------------------------------------------------------------------------------- /tests/server/README.md: -------------------------------------------------------------------------------- 1 | # Test Server 2 | 3 | It is used to start the test interface service, which can test the upload, websocket, login and other interfaces. 4 | 5 | ## Usage 6 | 7 | ```bash 8 | 9 | cd ./test/server 10 | 11 | yarn 12 | 13 | yarn start 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /src/components/Loading/src/typing.ts: -------------------------------------------------------------------------------- 1 | import { SizeEnum } from '/@/enums/sizeEnum'; 2 | 3 | export interface LoadingProps { 4 | tip: string; 5 | size: SizeEnum; 6 | absolute: boolean; 7 | loading: boolean; 8 | background: string; 9 | theme: 'dark' | 'light'; 10 | } 11 | -------------------------------------------------------------------------------- /src/settings/siteSetting.ts: -------------------------------------------------------------------------------- 1 | // github repo url 2 | export const GITHUB_URL = 'https://github.com/anncwb/vue-vben-admin'; 3 | 4 | // vue-vben-admin-next-doc 5 | export const DOC_URL = 'https://vvbin.cn/doc-next/'; 6 | 7 | // site url 8 | export const SITE_URL = 'https://vvbin.cn/next/'; 9 | -------------------------------------------------------------------------------- /src/enums/pageEnum.ts: -------------------------------------------------------------------------------- 1 | export enum PageEnum { 2 | // basic login path 3 | BASE_LOGIN = '/login', 4 | // basic home path 5 | BASE_HOME = '/bot', 6 | // error page path 7 | ERROR_PAGE = '/exception', 8 | // error log page path 9 | ERROR_LOG_PAGE = '/error-log/list', 10 | } 11 | -------------------------------------------------------------------------------- /src/api/demo/error.ts: -------------------------------------------------------------------------------- 1 | import { defHttp } from '/@/utils/http/axios'; 2 | 3 | enum Api { 4 | // The address does not exist 5 | Error = '/error', 6 | } 7 | 8 | /** 9 | * @description: Trigger ajax error 10 | */ 11 | 12 | export const fireErrorApi = () => defHttp.get({ url: Api.Error }); 13 | -------------------------------------------------------------------------------- /src/components/CountDown/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import countButton from './src/CountButton.vue'; 3 | import countdownInput from './src/CountdownInput.vue'; 4 | 5 | export const CountdownInput = withInstall(countdownInput); 6 | export const CountButton = withInstall(countButton); 7 | -------------------------------------------------------------------------------- /src/utils/log.ts: -------------------------------------------------------------------------------- 1 | const projectName = import.meta.env.VITE_GLOB_APP_TITLE; 2 | 3 | export function warn(message: string) { 4 | console.warn(`[${projectName} warn]:${message}`); 5 | } 6 | 7 | export function error(message: string) { 8 | throw new Error(`[${projectName} error]:${message}`); 9 | } 10 | -------------------------------------------------------------------------------- /dist/assets/useWindowSizeFn.48fcda03.js: -------------------------------------------------------------------------------- 1 | import{bz as o,af as a,T as d}from"./vendor.a45ea33e.js";function z(s,i=150,n){let e=()=>{s()};e=d(e,i);const t=()=>{n&&n.immediate&&e(),window.addEventListener("resize",e)},r=()=>{window.removeEventListener("resize",e)};return o(()=>{t()}),a(()=>{r()}),[t,r]}export{z as u}; 2 | -------------------------------------------------------------------------------- /dist/assets/index.05bbcef9.css: -------------------------------------------------------------------------------- 1 | .vben-botSwitch__avatar{margin:0 0 0 10px}.vben-messageSendBox__sendbox{left:0;right:0}.vben-messageSendBox__action{margin:0 0 0 10px}.vben-logWebsocket__msg>span{font-size:20px}.vben-groupSelect__select{width:80%}.vben-groupMemberSelect__select{width:80%}.vben-friendSelect__select{width:80%} 2 | -------------------------------------------------------------------------------- /src/components/Application/src/search/AppSearchKeyItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 12 | -------------------------------------------------------------------------------- /src/logics/theme/updateColorWeak.ts: -------------------------------------------------------------------------------- 1 | import { toggleClass } from './util'; 2 | 3 | /** 4 | * Change the status of the project's color weakness mode 5 | * @param colorWeak 6 | */ 7 | export function updateColorWeak(colorWeak: boolean) { 8 | toggleClass(colorWeak, 'color-weak', document.documentElement); 9 | } 10 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "type": "chrome", 6 | "request": "launch", 7 | "name": "Launch Chrome", 8 | "url": "http://localhost:3100", 9 | "webRoot": "${workspaceFolder}/src", 10 | "sourceMaps": true 11 | } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /dist/assets/index.59e5fe5a.css: -------------------------------------------------------------------------------- 1 | .vben-plugin__info{font-size:20px;font-family:"\5fae\8f6f\96c5\9ed1",Arial}.vben-plugin__banner{margin-right:12px}.vben-plugin__brief{padding:0 0 0 10px;font-size:20px}.vben-plugin__usage{padding:0 0 0 10px;font-size:20px;font-weight:bold;white-space:pre-wrap}.vben-plugin__action{margin:0 0 0 10px} 2 | -------------------------------------------------------------------------------- /src/components/Markdown/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import markDown from './src/Markdown.vue'; 3 | import markDownViewer from './src/MarkdownViewer.vue'; 4 | 5 | export const MarkDown = withInstall(markDown); 6 | export const MarkdownViewer = withInstall(markDownViewer); 7 | export * from './src/typing'; 8 | -------------------------------------------------------------------------------- /src/hooks/web/useAppInject.ts: -------------------------------------------------------------------------------- 1 | import { useAppProviderContext } from '/@/components/Application'; 2 | import { computed, unref } from 'vue'; 3 | 4 | export function useAppInject() { 5 | const values = useAppProviderContext(); 6 | 7 | return { 8 | getIsMobile: computed(() => unref(values.isMobile)), 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing Guide 2 | 3 | 1. Make sure you put things in the right category! 4 | 2. Always add your items to the end of a list. To be fair, the order is first-come-first-serve. 5 | 3. If you think something belongs in the wrong category, or think there needs to be a new category, feel free to edit things too. 6 | -------------------------------------------------------------------------------- /src/components/Cropper/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import cropperImage from './src/Cropper.vue'; 3 | import avatarCropper from './src/CropperAvatar.vue'; 4 | 5 | export * from './src/typing'; 6 | export const CropperImage = withInstall(cropperImage); 7 | export const CropperAvatar = withInstall(avatarCropper); 8 | -------------------------------------------------------------------------------- /src/components/Verify/src/typing.ts: -------------------------------------------------------------------------------- 1 | export interface DragVerifyActionType { 2 | resume: () => void; 3 | } 4 | 5 | export interface PassingData { 6 | isPassing: boolean; 7 | time: number; 8 | } 9 | 10 | export interface MoveData { 11 | event: MouseEvent | TouchEvent; 12 | moveDistance: number; 13 | moveX: number; 14 | } 15 | -------------------------------------------------------------------------------- /src/components/CodeEditor/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import codeEditor from './src/CodeEditor.vue'; 3 | import jsonPreview from './src/json-preview/JsonPreview.vue'; 4 | 5 | export const CodeEditor = withInstall(codeEditor); 6 | export const JsonPreview = withInstall(jsonPreview); 7 | 8 | export * from './src/typing'; 9 | -------------------------------------------------------------------------------- /src/design/transition/index.less: -------------------------------------------------------------------------------- 1 | @import './base.less'; 2 | @import './fade.less'; 3 | @import './scale.less'; 4 | @import './slide.less'; 5 | @import './scroll.less'; 6 | @import './zoom.less'; 7 | 8 | .collapse-transition { 9 | transition: 0.2s height ease-in-out, 0.2s padding-top ease-in-out, 0.2s padding-bottom ease-in-out; 10 | } 11 | -------------------------------------------------------------------------------- /src/views/bot/log/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "octref.vetur", 4 | "dbaeumer.vscode-eslint", 5 | "stylelint.vscode-stylelint", 6 | "esbenp.prettier-vscode", 7 | "mrmlnc.vscode-less", 8 | "lokalise.i18n-ally", 9 | "antfu.iconify", 10 | "mikestead.dotenv", 11 | "heybourn.headwind" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Verify/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils/index'; 2 | import basicDragVerify from './src/DragVerify.vue'; 3 | import rotateDragVerify from './src/ImgRotate.vue'; 4 | 5 | export const BasicDragVerify = withInstall(basicDragVerify); 6 | export const RotateDragVerify = withInstall(rotateDragVerify); 7 | export * from './src/typing'; 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset=utf-8 5 | end_of_line=lf 6 | insert_final_newline=true 7 | indent_style=space 8 | indent_size=2 9 | max_line_length = 100 10 | 11 | [*.{yml,yaml,json}] 12 | indent_style = space 13 | indent_size = 2 14 | 15 | [*.md] 16 | trim_trailing_whitespace = false 17 | 18 | [Makefile] 19 | indent_style = tab 20 | -------------------------------------------------------------------------------- /src/components/CodeEditor/src/json-preview/JsonPreview.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /src/components/Modal/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import './src/index.less'; 3 | import basicModal from './src/BasicModal.vue'; 4 | 5 | export const BasicModal = withInstall(basicModal); 6 | export { useModalContext } from './src/hooks/useModalContext'; 7 | export { useModal, useModalInner } from './src/hooks/useModal'; 8 | export * from './src/typing'; 9 | -------------------------------------------------------------------------------- /src/components/Page/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | 3 | import pageFooter from './src/PageFooter.vue'; 4 | import pageWrapper from './src/PageWrapper.vue'; 5 | 6 | export const PageFooter = withInstall(pageFooter); 7 | export const PageWrapper = withInstall(pageWrapper); 8 | 9 | export const PageWrapperFixedHeightKey = 'PageWrapperFixedHeight'; 10 | -------------------------------------------------------------------------------- /src/directives/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Configure and register global directives 3 | */ 4 | import type { App } from 'vue'; 5 | import { setupPermissionDirective } from './permission'; 6 | import { setupLoadingDirective } from './loading'; 7 | 8 | export function setupGlobDirectives(app: App) { 9 | setupPermissionDirective(app); 10 | setupLoadingDirective(app); 11 | } 12 | -------------------------------------------------------------------------------- /src/components/Basic/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import basicArrow from './src/BasicArrow.vue'; 3 | import basicTitle from './src/BasicTitle.vue'; 4 | import basicHelp from './src/BasicHelp.vue'; 5 | 6 | export const BasicArrow = withInstall(basicArrow); 7 | export const BasicTitle = withInstall(basicTitle); 8 | export const BasicHelp = withInstall(basicHelp); 9 | -------------------------------------------------------------------------------- /src/components/Qrcode/src/toCanvas.ts: -------------------------------------------------------------------------------- 1 | import { renderQrCode } from './drawCanvas'; 2 | import { drawLogo } from './drawLogo'; 3 | import { RenderQrCodeParams } from './typing'; 4 | export const toCanvas = (options: RenderQrCodeParams) => { 5 | return renderQrCode(options) 6 | .then(() => { 7 | return options; 8 | }) 9 | .then(drawLogo) as Promise; 10 | }; 11 | -------------------------------------------------------------------------------- /src/design/transition/base.less: -------------------------------------------------------------------------------- 1 | .transition-default() { 2 | &-enter-active, 3 | &-leave-active { 4 | transition: 0.3s cubic-bezier(0.25, 0.8, 0.5, 1) !important; 5 | } 6 | 7 | &-move { 8 | transition: transform 0.4s; 9 | } 10 | } 11 | 12 | .expand-transition { 13 | .transition-default(); 14 | } 15 | 16 | .expand-x-transition { 17 | .transition-default(); 18 | } 19 | -------------------------------------------------------------------------------- /dist/assets/LockModal.0068f88c.css: -------------------------------------------------------------------------------- 1 | .vben-header-lock-modal__entry{position:relative;padding:130px 30px 30px;border-radius:10px}.vben-header-lock-modal__header{position:absolute;top:0;left:calc(50% - 45px);width:auto;text-align:center}.vben-header-lock-modal__header-img{width:70px;border-radius:50%}.vben-header-lock-modal__header-name{margin-top:5px}.vben-header-lock-modal__footer{text-align:center} 2 | -------------------------------------------------------------------------------- /src/api/sys/menu.ts: -------------------------------------------------------------------------------- 1 | import { defHttp } from '/@/utils/http/axios'; 2 | import { getMenuListResultModel } from './model/menuModel'; 3 | 4 | enum Api { 5 | GetMenuList = '/getMenuList', 6 | } 7 | 8 | /** 9 | * @description: Get user menu based on id 10 | */ 11 | 12 | export const getMenuList = () => { 13 | return defHttp.get({ url: Api.GetMenuList }); 14 | }; 15 | -------------------------------------------------------------------------------- /src/components/Excel/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import impExcel from './src/ImportExcel.vue'; 3 | import expExcelModal from './src/ExportExcelModal.vue'; 4 | 5 | export const ImpExcel = withInstall(impExcel); 6 | export const ExpExcelModal = withInstall(expExcelModal); 7 | export * from './src/typing'; 8 | export { jsonToSheetXlsx, aoaToSheetXlsx } from './src/Export2Excel'; 9 | -------------------------------------------------------------------------------- /src/views/dashboard/analysis/components/props.ts: -------------------------------------------------------------------------------- 1 | import { PropType } from 'vue'; 2 | 3 | export interface BasicProps { 4 | width: string; 5 | height: string; 6 | } 7 | export const basicProps = { 8 | width: { 9 | type: String as PropType, 10 | default: '100%', 11 | }, 12 | height: { 13 | type: String as PropType, 14 | default: '280px', 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /dist/assets/index.6ce36d9e.css: -------------------------------------------------------------------------------- 1 | .vben-layout-footer[data-v-9b3e8618]{color:#d9d9d9;text-align:center}.vben-layout-footer__links[data-v-9b3e8618]{margin-bottom:8px}.vben-layout-footer__links a[data-v-9b3e8618]{color:#d9d9d9}.vben-layout-footer__links a[data-v-9b3e8618]:hover{color:#000000d9}.vben-layout-footer__github[data-v-9b3e8618]{margin:0 30px}.vben-layout-footer__github[data-v-9b3e8618]:hover{color:#000000d9} 2 | -------------------------------------------------------------------------------- /src/hooks/core/onMountedOrActivated.ts: -------------------------------------------------------------------------------- 1 | import { nextTick, onMounted, onActivated } from 'vue'; 2 | 3 | export function onMountedOrActivated(hook: Fn) { 4 | let mounted: boolean; 5 | 6 | onMounted(() => { 7 | hook(); 8 | nextTick(() => { 9 | mounted = true; 10 | }); 11 | }); 12 | 13 | onActivated(() => { 14 | if (mounted) { 15 | hook(); 16 | } 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Discord Chat 4 | url: https://discord.gg/8GuAdwDhj6 5 | about: Ask questions and discuss with other Vben users in real time. 6 | - name: Questions & Discussions 7 | url: https://github.com/anncwb/vue-vben-admin/discussions 8 | about: Use GitHub discussions for message-board style questions and discussions. 9 | -------------------------------------------------------------------------------- /src/components/FlowChart/src/types.ts: -------------------------------------------------------------------------------- 1 | import { NodeConfig } from '@logicflow/core'; 2 | import { ToolbarTypeEnum } from './enum'; 3 | 4 | export interface NodeItem extends NodeConfig { 5 | icon: string; 6 | } 7 | 8 | export interface ToolbarConfig { 9 | type?: string | ToolbarTypeEnum; 10 | tooltip?: string | boolean; 11 | icon?: string; 12 | disabled?: boolean; 13 | separate?: boolean; 14 | } 15 | -------------------------------------------------------------------------------- /dist/app.config.js: -------------------------------------------------------------------------------- 1 | window.__PRODUCTION__ZEROBOT_PLUGIN_WEBUI__CONF__={"VITE_GLOB_APP_TITLE":"ZeroBot_Plugin_Webui","VITE_GLOB_APP_SHORT_NAME":"ZeroBot_Plugin_Webui","VITE_GLOB_API_URL":"/api","VITE_GLOB_UPLOAD_URL":"/upload","VITE_GLOB_API_URL_PREFIX":""};Object.freeze(window.__PRODUCTION__ZEROBOT_PLUGIN_WEBUI__CONF__);Object.defineProperty(window,"__PRODUCTION__ZEROBOT_PLUGIN_WEBUI__CONF__",{configurable:false,writable:false,}); -------------------------------------------------------------------------------- /src/design/transition/scale.less: -------------------------------------------------------------------------------- 1 | .scale-transition { 2 | .transition-default(); 3 | 4 | &-enter-from, 5 | &-leave, 6 | &-leave-to { 7 | opacity: 0%; 8 | transform: scale(0); 9 | } 10 | } 11 | 12 | .scale-rotate-transition { 13 | .transition-default(); 14 | 15 | &-enter-from, 16 | &-leave, 17 | &-leave-to { 18 | opacity: 0%; 19 | transform: scale(0) rotate(-45deg); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/locales/lang/en.ts: -------------------------------------------------------------------------------- 1 | import { genMessage } from '../helper'; 2 | import antdLocale from 'ant-design-vue/es/locale/en_US'; 3 | // import momentLocale from 'moment/dist/locale/en-us'; 4 | 5 | const modules = import.meta.globEager('./en/**/*.ts'); 6 | export default { 7 | message: { 8 | ...genMessage(modules, 'en'), 9 | antdLocale, 10 | }, 11 | momentLocale: null, 12 | momentLocaleName: 'en', 13 | }; 14 | -------------------------------------------------------------------------------- /src/locales/lang/zh-CN/common.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | okText: '确认', 3 | closeText: '关闭', 4 | cancelText: '取消', 5 | loadingText: '加载中...', 6 | saveText: '保存', 7 | delText: '删除', 8 | resetText: '重置', 9 | searchText: '搜索', 10 | queryText: '查询', 11 | 12 | inputText: '请输入', 13 | chooseText: '请选择', 14 | 15 | redo: '刷新', 16 | back: '返回', 17 | 18 | light: '亮色主题', 19 | dark: '黑暗主题', 20 | }; 21 | -------------------------------------------------------------------------------- /tests/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": false, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es6", 9 | "sourceMap": false, 10 | "esModuleInterop": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./" 13 | }, 14 | "exclude": ["node_modules"] 15 | } 16 | -------------------------------------------------------------------------------- /src/components/Scrollbar/src/types.d.ts: -------------------------------------------------------------------------------- 1 | export interface BarMapItem { 2 | offset: string; 3 | scroll: string; 4 | scrollSize: string; 5 | size: string; 6 | key: string; 7 | axis: string; 8 | client: string; 9 | direction: string; 10 | } 11 | export interface BarMap { 12 | vertical: BarMapItem; 13 | horizontal: BarMapItem; 14 | } 15 | 16 | export interface ScrollbarType { 17 | wrap: ElRef; 18 | } 19 | -------------------------------------------------------------------------------- /src/locales/lang/zh_CN.ts: -------------------------------------------------------------------------------- 1 | import { genMessage } from '../helper'; 2 | import antdLocale from 'ant-design-vue/es/locale/zh_CN'; 3 | import momentLocale from 'moment/dist/locale/zh-cn'; 4 | 5 | const modules = import.meta.globEager('./zh-CN/**/*.ts'); 6 | export default { 7 | message: { 8 | ...genMessage(modules, 'zh-CN'), 9 | antdLocale, 10 | }, 11 | momentLocale, 12 | momentLocaleName: 'zh-cn', 13 | }; 14 | -------------------------------------------------------------------------------- /.husky/lintstagedrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | '*.{js,jsx,ts,tsx}': ['eslint --fix', 'prettier --write'], 3 | '{!(package)*.json,*.code-snippets,.!(browserslist)*rc}': ['prettier --write--parser json'], 4 | 'package.json': ['prettier --write'], 5 | '*.vue': ['eslint --fix', 'prettier --write', 'stylelint --fix'], 6 | '*.{scss,less,styl,html}': ['stylelint --fix', 'prettier --write'], 7 | '*.md': ['prettier --write'], 8 | }; 9 | -------------------------------------------------------------------------------- /src/components/Form/src/hooks/useComponentRegister.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentType } from '../types/index'; 2 | import { tryOnUnmounted } from '@vueuse/core'; 3 | import { add, del } from '../componentMap'; 4 | import type { Component } from 'vue'; 5 | 6 | export function useComponentRegister(compName: ComponentType, comp: Component) { 7 | add(compName, comp); 8 | tryOnUnmounted(() => { 9 | del(compName); 10 | }); 11 | } 12 | -------------------------------------------------------------------------------- /src/settings/encryptionSetting.ts: -------------------------------------------------------------------------------- 1 | import { isDevMode } from '/@/utils/env'; 2 | 3 | // System default cache time, in seconds 4 | export const DEFAULT_CACHE_TIME = 60 * 60 * 24 * 7; 5 | 6 | // aes encryption key 7 | export const cacheCipher = { 8 | key: '_11111000001111@', 9 | iv: '@11111000001111_', 10 | }; 11 | 12 | // Whether the system cache is encrypted using aes 13 | export const enableStorageEncryption = !isDevMode(); 14 | -------------------------------------------------------------------------------- /src/router/routes/mainOut.ts: -------------------------------------------------------------------------------- 1 | /** 2 | The routing of this file will not show the layout. 3 | It is an independent new page. 4 | the contents of the file still need to log in to access 5 | */ 6 | import type { AppRouteModule } from '/@/router/types'; 7 | 8 | // test 9 | // http:ip:port/main-out 10 | export const mainOutRoutes: AppRouteModule[] = []; 11 | 12 | export const mainOutRouteNames = mainOutRoutes.map((item) => item.name); 13 | -------------------------------------------------------------------------------- /src/api/sys/model/menuModel.ts: -------------------------------------------------------------------------------- 1 | import type { RouteMeta } from 'vue-router'; 2 | export interface RouteItem { 3 | path: string; 4 | component: any; 5 | meta: RouteMeta; 6 | name?: string; 7 | alias?: string | string[]; 8 | redirect?: string; 9 | caseSensitive?: boolean; 10 | children?: RouteItem[]; 11 | } 12 | 13 | /** 14 | * @description: Get menu return value 15 | */ 16 | export type getMenuListResultModel = RouteItem[]; 17 | -------------------------------------------------------------------------------- /tests/server/controller/UserController.ts: -------------------------------------------------------------------------------- 1 | import UserService from '../service/UserService'; 2 | 3 | class UserController { 4 | private service: UserService = new UserService(); 5 | 6 | login = async (ctx) => { 7 | ctx.body = await this.service.login(); 8 | }; 9 | 10 | getUserInfoById = async (ctx) => { 11 | ctx.body = await this.service.getUserInfoById(); 12 | }; 13 | } 14 | 15 | export default new UserController(); 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .npmrc 4 | .cache 5 | 6 | tests/server/static 7 | tests/server/static/upload 8 | 9 | .local 10 | # local env files 11 | .env.local 12 | .env.*.local 13 | .eslintcache 14 | 15 | # Log files 16 | npm-debug.log* 17 | yarn-debug.log* 18 | yarn-error.log* 19 | pnpm-debug.log* 20 | 21 | # Editor directories and files 22 | .idea 23 | # .vscode 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /dist/assets/index.4b6a1a28.js: -------------------------------------------------------------------------------- 1 | import{A as c,a as p,u,B as _,D as i}from"./vendor.a45ea33e.js";const h=c({setup(l){const{currentRoute:o,replace:a}=p(),{params:e,query:s}=u(o),{path:r,_redirect_type:n="path"}=e;Reflect.deleteProperty(e,"_redirect_type"),Reflect.deleteProperty(e,"path");const t=Array.isArray(r)?r.join("/"):r;return a(n==="name"?{name:t,query:s,params:e}:{path:t.startsWith("/")?t:"/"+t,query:s}),(d,f)=>(_(),i("div"))}});export{h as default}; 2 | -------------------------------------------------------------------------------- /dist/assets/index.0935299d.css: -------------------------------------------------------------------------------- 1 | .vben-iframe-page .ant-spin-nested-loading[data-v-7cb5d70d]{position:relative;height:100%}.vben-iframe-page .ant-spin-nested-loading .ant-spin-container[data-v-7cb5d70d]{width:100%;height:100%;padding:10px}.vben-iframe-page__mask[data-v-7cb5d70d]{position:absolute;top:0;left:0;width:100%;height:100%}.vben-iframe-page__main[data-v-7cb5d70d]{width:100%;height:100%;overflow:hidden;background-color:#fff;border:0;box-sizing:border-box} 2 | -------------------------------------------------------------------------------- /src/components/FlowChart/src/useFlowContext.ts: -------------------------------------------------------------------------------- 1 | import type LogicFlow from '@logicflow/core'; 2 | 3 | import { provide, inject } from 'vue'; 4 | 5 | const key = Symbol('flow-chart'); 6 | 7 | type Instance = { 8 | logicFlow: LogicFlow; 9 | }; 10 | 11 | export function createFlowChartContext(instance: Instance) { 12 | provide(key, instance); 13 | } 14 | 15 | export function useFlowChartContext(): Instance { 16 | return inject(key) as Instance; 17 | } 18 | -------------------------------------------------------------------------------- /src/design/ant/input.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../color.less'; 2 | 3 | // input 4 | .ant-input { 5 | &-number { 6 | min-width: 110px; 7 | } 8 | } 9 | 10 | .ant-input-affix-wrapper .ant-input-suffix { 11 | right: 9px; 12 | } 13 | 14 | .ant-input-clear-icon { 15 | margin-right: 5px; 16 | } 17 | 18 | .ant-input-affix-wrapper-textarea-with-clear-btn { 19 | padding: 0 !important; 20 | 21 | textarea.ant-input { 22 | padding: 4px; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/locales/lang/en/common.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | okText: 'OK', 3 | closeText: 'Close', 4 | cancelText: 'Cancel', 5 | loadingText: 'Loading...', 6 | saveText: 'Save', 7 | delText: 'Delete', 8 | resetText: 'Reset', 9 | searchText: 'Search', 10 | queryText: 'Search', 11 | 12 | inputText: 'Please enter', 13 | chooseText: 'Please choose', 14 | 15 | redo: 'Refresh', 16 | back: 'Back', 17 | 18 | light: 'Light', 19 | dark: 'Dark', 20 | }; 21 | -------------------------------------------------------------------------------- /.github/workflows/issue-close-require.yml: -------------------------------------------------------------------------------- 1 | name: Issue Close Require 2 | 3 | on: 4 | schedule: 5 | - cron: '0 0 * * *' 6 | 7 | jobs: 8 | close-issues: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: need reproduction 12 | uses: actions-cool/issues-helper@v2.1.1 13 | with: 14 | actions: 'close-issues' 15 | token: ${{ secrets.OPER_TOKEN }} 16 | labels: 'need reproduction' 17 | inactive-day: 3 18 | -------------------------------------------------------------------------------- /dist/assets/ThemeColorPicker.8eb61909.css: -------------------------------------------------------------------------------- 1 | .vben-setting-theme-picker{display:flex;flex-wrap:wrap;margin:16px 0;justify-content:space-around}.vben-setting-theme-picker__item{width:20px;height:20px;cursor:pointer;border:1px solid #ddd;border-radius:2px}.vben-setting-theme-picker__item svg{display:none}.vben-setting-theme-picker__item--active{border:1px solid #0b79ee}.vben-setting-theme-picker__item--active svg{display:inline-block;margin:0 0 3px 3px;font-size:12px;fill:#fff!important} 2 | -------------------------------------------------------------------------------- /tests/server/ecosystem.config.js: -------------------------------------------------------------------------------- 1 | const { name } = require('./package.json'); 2 | const path = require('path'); 3 | 4 | module.exports = { 5 | apps: [ 6 | { 7 | name, 8 | script: path.resolve(__dirname, './dist/index.js'), 9 | instances: require('os').cpus().length, 10 | autorestart: true, 11 | watch: true, 12 | env_production: { 13 | NODE_ENV: 'production', 14 | PORT: 8080, 15 | }, 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/Button/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import type { ExtractPropTypes } from 'vue'; 3 | import button from './src/BasicButton.vue'; 4 | import popConfirmButton from './src/PopConfirmButton.vue'; 5 | import { buttonProps } from './src/props'; 6 | 7 | export const Button = withInstall(button); 8 | export const PopConfirmButton = withInstall(popConfirmButton); 9 | export declare type ButtonProps = Partial>; 10 | -------------------------------------------------------------------------------- /src/directives/ripple/index.less: -------------------------------------------------------------------------------- 1 | .ripple-container { 2 | position: absolute; 3 | top: 0; 4 | left: 0; 5 | width: 0; 6 | height: 0; 7 | overflow: hidden; 8 | pointer-events: none; 9 | } 10 | 11 | .ripple-effect { 12 | position: relative; 13 | z-index: 9999; 14 | width: 1px; 15 | height: 1px; 16 | margin-top: 0; 17 | margin-left: 0; 18 | pointer-events: none; 19 | border-radius: 50%; 20 | transition: all 0.6s cubic-bezier(0.4, 0, 0.2, 1); 21 | } 22 | -------------------------------------------------------------------------------- /src/views/sys/lock/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 14 | -------------------------------------------------------------------------------- /src/components/registerGlobComp.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue'; 2 | import { Button } from './Button'; 3 | import { 4 | // Need 5 | Button as AntButton, 6 | Input, 7 | Layout, 8 | } from 'ant-design-vue'; 9 | 10 | const compList = [AntButton.Group]; 11 | 12 | export function registerGlobComp(app: App) { 13 | compList.forEach((comp) => { 14 | app.component(comp.name || comp.displayName, comp); 15 | }); 16 | 17 | app.use(Input).use(Button).use(Layout); 18 | } 19 | -------------------------------------------------------------------------------- /src/hooks/core/useRefs.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue'; 2 | import { ref, onBeforeUpdate } from 'vue'; 3 | 4 | export function useRefs(): [Ref, (index: number) => (el: HTMLElement) => void] { 5 | const refs = ref([]) as Ref; 6 | 7 | onBeforeUpdate(() => { 8 | refs.value = []; 9 | }); 10 | 11 | const setRefs = (index: number) => (el: HTMLElement) => { 12 | refs.value[index] = el; 13 | }; 14 | 15 | return [refs, setRefs]; 16 | } 17 | -------------------------------------------------------------------------------- /src/logics/theme/util.ts: -------------------------------------------------------------------------------- 1 | const docEle = document.documentElement; 2 | export function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) { 3 | const targetEl = target || document.body; 4 | let { className } = targetEl; 5 | className = className.replace(clsName, ''); 6 | targetEl.className = flag ? `${className} ${clsName} ` : className; 7 | } 8 | 9 | export function setCssVar(prop: string, val: any, dom = docEle) { 10 | dom.style.setProperty(prop, val); 11 | } 12 | -------------------------------------------------------------------------------- /src/components/Container/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import collapseContainer from './src/collapse/CollapseContainer.vue'; 3 | import scrollContainer from './src/ScrollContainer.vue'; 4 | import lazyContainer from './src/LazyContainer.vue'; 5 | 6 | export const CollapseContainer = withInstall(collapseContainer); 7 | export const ScrollContainer = withInstall(scrollContainer); 8 | export const LazyContainer = withInstall(lazyContainer); 9 | 10 | export * from './src/typing'; 11 | -------------------------------------------------------------------------------- /dist/assets/index.742490d2.js: -------------------------------------------------------------------------------- 1 | import{A as r,j as n,B as t,a1 as s,a5 as m,u as f,ac as i,a2 as p}from"./vendor.a45ea33e.js";import u from"./LockPage.b2ea75f1.js";import{u as d}from"./lock.0c0efc31.js";import"./index.c19540a9.js";import"./header.d801b988.js";const x=r({setup(k){const e=d(),c=n(()=>{var o,a;return(a=(o=e==null?void 0:e.getLockInfo)==null?void 0:o.isLock)!=null?a:!1});return(o,a)=>(t(),s(p,{name:"fade-bottom",mode:"out-in"},{default:m(()=>[f(c)?(t(),s(u,{key:0})):i("",!0)]),_:1}))}});export{x as default}; 2 | -------------------------------------------------------------------------------- /src/components/Table/src/components/EditTableHeaderIcon.vue: -------------------------------------------------------------------------------- 1 | 8 | 17 | -------------------------------------------------------------------------------- /src/hooks/core/useLockFn.ts: -------------------------------------------------------------------------------- 1 | import { ref, unref } from 'vue'; 2 | 3 | export function useLockFn

(fn: (...args: P) => Promise) { 4 | const lockRef = ref(false); 5 | return async function (...args: P) { 6 | if (unref(lockRef)) return; 7 | lockRef.value = true; 8 | try { 9 | const ret = await fn(...args); 10 | lockRef.value = false; 11 | return ret; 12 | } catch (e) { 13 | lockRef.value = false; 14 | throw e; 15 | } 16 | }; 17 | } 18 | -------------------------------------------------------------------------------- /src/components/ClickOutSide/src/ClickOutSide.vue: -------------------------------------------------------------------------------- 1 | 6 | 20 | -------------------------------------------------------------------------------- /src/components/Container/src/typing.ts: -------------------------------------------------------------------------------- 1 | export type ScrollType = 'default' | 'main'; 2 | 3 | export interface CollapseContainerOptions { 4 | canExpand?: boolean; 5 | title?: string; 6 | helpMessage?: Array | string; 7 | } 8 | export interface ScrollContainerOptions { 9 | enableScroll?: boolean; 10 | type?: ScrollType; 11 | } 12 | 13 | export type ScrollActionType = RefType<{ 14 | scrollBottom: () => void; 15 | getScrollWrap: () => Nullable; 16 | scrollTo: (top: number) => void; 17 | }>; 18 | -------------------------------------------------------------------------------- /src/components/Tree/src/TreeIcon.ts: -------------------------------------------------------------------------------- 1 | import type { VNode, FunctionalComponent } from 'vue'; 2 | 3 | import { h } from 'vue'; 4 | import { isString } from '/@/utils/is'; 5 | import { Icon } from '/@/components/Icon'; 6 | 7 | export interface ComponentProps { 8 | icon: VNode | string; 9 | } 10 | 11 | export const TreeIcon: FunctionalComponent = ({ icon }: ComponentProps) => { 12 | if (!icon) return null; 13 | if (isString(icon)) { 14 | return h(Icon, { icon, class: 'mr-1' }); 15 | } 16 | return Icon; 17 | }; 18 | -------------------------------------------------------------------------------- /src/hooks/web/useContextMenu.ts: -------------------------------------------------------------------------------- 1 | import { onUnmounted, getCurrentInstance } from 'vue'; 2 | import { createContextMenu, destroyContextMenu } from '/@/components/ContextMenu'; 3 | import type { ContextMenuItem } from '/@/components/ContextMenu'; 4 | export type { ContextMenuItem }; 5 | export function useContextMenu(authRemove = true) { 6 | if (getCurrentInstance() && authRemove) { 7 | onUnmounted(() => { 8 | destroyContextMenu(); 9 | }); 10 | } 11 | return [createContextMenu, destroyContextMenu]; 12 | } 13 | -------------------------------------------------------------------------------- /tests/test.spec.ts: -------------------------------------------------------------------------------- 1 | // import { mount } from '@vue/test-utils'; 2 | // import { Button } from '/@/components/Button'; 3 | 4 | test('if jest is normal.', async () => { 5 | expect('jest').toEqual('jest'); 6 | }); 7 | 8 | // TODO Vue component testing is not supported temporarily 9 | // test('is a Vue instance.', async () => { 10 | // const wrapper = mount(Button, { 11 | // slots: { 12 | // default: 'Button text', 13 | // }, 14 | // }); 15 | // expect(wrapper.html()).toContain('Button text'); 16 | // }); 17 | -------------------------------------------------------------------------------- /tests/server/controller/FileController.ts: -------------------------------------------------------------------------------- 1 | import FileService from '../service/FileService'; 2 | 3 | class FileController { 4 | private service: FileService = new FileService(); 5 | 6 | upload = async (ctx) => { 7 | const files = ctx.request.files.file; 8 | console.log(files); 9 | 10 | if (files.length === undefined) { 11 | this.service.upload(ctx, files, false); 12 | } else { 13 | this.service.upload(ctx, files, true); 14 | } 15 | }; 16 | } 17 | 18 | export default new FileController(); 19 | -------------------------------------------------------------------------------- /src/enums/sizeEnum.ts: -------------------------------------------------------------------------------- 1 | export enum SizeEnum { 2 | DEFAULT = 'default', 3 | SMALL = 'small', 4 | LARGE = 'large', 5 | } 6 | 7 | export enum SizeNumberEnum { 8 | DEFAULT = 48, 9 | SMALL = 16, 10 | LARGE = 64, 11 | } 12 | 13 | export const sizeMap: Map = (() => { 14 | const map = new Map(); 15 | map.set(SizeEnum.DEFAULT, SizeNumberEnum.DEFAULT); 16 | map.set(SizeEnum.SMALL, SizeNumberEnum.SMALL); 17 | map.set(SizeEnum.LARGE, SizeNumberEnum.LARGE); 18 | return map; 19 | })(); 20 | -------------------------------------------------------------------------------- /tests/server/routes.ts: -------------------------------------------------------------------------------- 1 | import UserController from './controller/UserController'; 2 | import FileController from './controller/FileController'; 3 | 4 | export default [ 5 | // user 6 | { 7 | path: '/login', 8 | method: 'post', 9 | action: UserController.login, 10 | }, 11 | { 12 | path: '/getUserInfoById', 13 | method: 'get', 14 | action: UserController.getUserInfoById, 15 | }, 16 | 17 | // file 18 | { 19 | path: '/upload', 20 | method: 'post', 21 | action: FileController.upload, 22 | }, 23 | ]; 24 | -------------------------------------------------------------------------------- /src/components/Modal/src/hooks/useModalContext.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey } from 'vue'; 2 | import { createContext, useContext } from '/@/hooks/core/useContext'; 3 | 4 | export interface ModalContextProps { 5 | redoModalHeight: () => void; 6 | } 7 | 8 | const key: InjectionKey = Symbol(); 9 | 10 | export function createModalContext(context: ModalContextProps) { 11 | return createContext(context, key); 12 | } 13 | 14 | export function useModalContext() { 15 | return useContext(key); 16 | } 17 | -------------------------------------------------------------------------------- /mock/_createProductionServer.ts: -------------------------------------------------------------------------------- 1 | import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer'; 2 | 3 | const modules = import.meta.globEager('./**/*.ts'); 4 | 5 | const mockModules: any[] = []; 6 | Object.keys(modules).forEach((key) => { 7 | if (key.includes('/_')) { 8 | return; 9 | } 10 | mockModules.push(...modules[key].default); 11 | }); 12 | 13 | /** 14 | * Used in a production environment. Need to manually import all modules 15 | */ 16 | export function setupProdMockServer() { 17 | createProdMockServer(mockModules); 18 | } 19 | -------------------------------------------------------------------------------- /src/components/CardList/src/data.ts: -------------------------------------------------------------------------------- 1 | import { ref } from 'vue'; 2 | //每行个数 3 | export const grid = ref(12); 4 | // slider属性 5 | export const useSlider = (min = 6, max = 12) => { 6 | // 每行显示个数滑动条 7 | const getMarks = () => { 8 | const l = {}; 9 | for (let i = min; i < max + 1; i++) { 10 | l[i] = { 11 | style: { 12 | color: '#fff', 13 | }, 14 | label: i, 15 | }; 16 | } 17 | return l; 18 | }; 19 | return { 20 | min, 21 | max, 22 | marks: getMarks(), 23 | step: 1, 24 | }; 25 | }; 26 | -------------------------------------------------------------------------------- /src/api/bot/job.ts: -------------------------------------------------------------------------------- 1 | import { defHttp } from '/@/utils/http/axios'; 2 | import { Job, DeleteReq } from './model/job'; 3 | 4 | export enum JobApi { 5 | jobList = '/job/list', 6 | jobAdd = '/job/add', 7 | jobDelete = '/job/delete', 8 | } 9 | 10 | export function jobList() { 11 | return defHttp.get({ url: JobApi.jobList }); 12 | } 13 | 14 | export function jobAdd(params: Job) { 15 | return defHttp.post({ url: JobApi.jobAdd, params }); 16 | } 17 | 18 | export function jobDelete(params: DeleteReq) { 19 | return defHttp.post({ url: JobApi.jobDelete, params }); 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/socket/websocket.ts: -------------------------------------------------------------------------------- 1 | class Websocket1 { 2 | socket: WebSocket; 3 | constructor(url) { 4 | this.socket = new WebSocket(url); 5 | } 6 | 7 | onMessage(event) { 8 | this.socket.onmessage = event; 9 | } 10 | 11 | onOpen(event) { 12 | this.socket.onopen = event; 13 | } 14 | 15 | onError(event) { 16 | this.socket.onerror = event; 17 | } 18 | 19 | onClose(event) { 20 | this.socket.onclose = event; 21 | } 22 | 23 | send(message) { 24 | this.socket.send(JSON.stringify(message)); 25 | } 26 | } 27 | 28 | export default Websocket1; 29 | -------------------------------------------------------------------------------- /src/logics/theme/index.ts: -------------------------------------------------------------------------------- 1 | import { getThemeColors, generateColors } from '../../../build/config/themeConfig'; 2 | 3 | import { replaceStyleVariables } from 'vite-plugin-theme/es/client'; 4 | import { mixLighten, mixDarken, tinycolor } from 'vite-plugin-theme/es/colorUtils'; 5 | 6 | export async function changeTheme(color: string) { 7 | const colors = generateColors({ 8 | mixDarken, 9 | mixLighten, 10 | tinycolor, 11 | color, 12 | }); 13 | 14 | return await replaceStyleVariables({ 15 | colorVariables: [...getThemeColors(color), ...colors], 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /tests/server/service/UserService.ts: -------------------------------------------------------------------------------- 1 | import { Result } from '../utils'; 2 | 3 | const fakeUserInfo = { 4 | userId: '1', 5 | username: 'xiaoguofan', 6 | realName: 'xiaoguofan', 7 | desc: 'manager', 8 | password: '123456', 9 | token: 'fakeToken1', 10 | roles: [ 11 | { 12 | roleName: 'Super Admin', 13 | value: 'super', 14 | }, 15 | ], 16 | }; 17 | export default class UserService { 18 | async login() { 19 | return Result.success(fakeUserInfo); 20 | } 21 | 22 | async getUserInfoById() { 23 | return Result.success(fakeUserInfo); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/components/Form/src/hooks/useFormContext.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey } from 'vue'; 2 | import { createContext, useContext } from '/@/hooks/core/useContext'; 3 | 4 | export interface FormContextProps { 5 | resetAction: () => Promise; 6 | submitAction: () => Promise; 7 | } 8 | 9 | const key: InjectionKey = Symbol(); 10 | 11 | export function createFormContext(context: FormContextProps) { 12 | return createContext(context, key); 13 | } 14 | 15 | export function useFormContext() { 16 | return useContext(key); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Button/src/props.ts: -------------------------------------------------------------------------------- 1 | export const buttonProps = { 2 | color: { type: String, validator: (v) => ['error', 'warning', 'success', ''].includes(v) }, 3 | loading: { type: Boolean }, 4 | disabled: { type: Boolean }, 5 | /** 6 | * Text before icon. 7 | */ 8 | preIcon: { type: String }, 9 | /** 10 | * Text after icon. 11 | */ 12 | postIcon: { type: String }, 13 | /** 14 | * preIcon and postIcon icon size. 15 | * @default: 14 16 | */ 17 | iconSize: { type: Number, default: 14 }, 18 | onClick: { type: Function as PropType<(...args) => any>, default: null }, 19 | }; 20 | -------------------------------------------------------------------------------- /src/components/Menu/src/types.ts: -------------------------------------------------------------------------------- 1 | // import { ComputedRef } from 'vue'; 2 | // import { ThemeEnum } from '/@/enums/appEnum'; 3 | // import { MenuModeEnum } from '/@/enums/menuEnum'; 4 | export interface MenuState { 5 | // 默认选中的列表 6 | defaultSelectedKeys: string[]; 7 | 8 | // 模式 9 | // mode: MenuModeEnum; 10 | 11 | // // 主题 12 | // theme: ComputedRef | ThemeEnum; 13 | 14 | // 缩进 15 | inlineIndent?: number; 16 | 17 | // 展开数组 18 | openKeys: string[]; 19 | 20 | // 当前选中的菜单项 key 数组 21 | selectedKeys: string[]; 22 | 23 | // 收缩状态下展开的数组 24 | collapsedOpenKeys: string[]; 25 | } 26 | -------------------------------------------------------------------------------- /src/design/transition/zoom.less: -------------------------------------------------------------------------------- 1 | // zoom-out 2 | .zoom-out-enter-active, 3 | .zoom-out-leave-active { 4 | transition: opacity 0.1 ease-in-out, transform 0.15s ease-out; 5 | } 6 | 7 | .zoom-out-enter-from, 8 | .zoom-out-leave-to { 9 | opacity: 0%; 10 | transform: scale(0); 11 | } 12 | 13 | // zoom-fade 14 | .zoom-fade-enter-active, 15 | .zoom-fade-leave-active { 16 | transition: transform 0.2s, opacity 0.3s ease-out; 17 | } 18 | 19 | .zoom-fade-enter-from { 20 | opacity: 0%; 21 | transform: scale(0.92); 22 | } 23 | 24 | .zoom-fade-leave-to { 25 | opacity: 0%; 26 | transform: scale(1.06); 27 | } 28 | -------------------------------------------------------------------------------- /src/enums/exceptionEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: Exception related enumeration 3 | */ 4 | export enum ExceptionEnum { 5 | // page not access 6 | PAGE_NOT_ACCESS = 403, 7 | 8 | // page not found 9 | PAGE_NOT_FOUND = 404, 10 | 11 | // error 12 | ERROR = 500, 13 | 14 | // net work error 15 | NET_WORK_ERROR = 10000, 16 | 17 | // No data on the page. In fact, it is not an exception page 18 | PAGE_NOT_DATA = 10100, 19 | } 20 | 21 | export enum ErrorTypeEnum { 22 | VUE = 'vue', 23 | SCRIPT = 'script', 24 | RESOURCE = 'resource', 25 | AJAX = 'ajax', 26 | PROMISE = 'promise', 27 | } 28 | -------------------------------------------------------------------------------- /src/layouts/default/header/components/index.ts: -------------------------------------------------------------------------------- 1 | import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; 2 | import FullScreen from './FullScreen.vue'; 3 | 4 | export const UserDropDown = createAsyncComponent(() => import('./user-dropdown/index.vue'), { 5 | loading: true, 6 | }); 7 | 8 | export const LayoutBreadcrumb = createAsyncComponent(() => import('./Breadcrumb.vue')); 9 | 10 | export const Notify = createAsyncComponent(() => import('./notify/index.vue')); 11 | 12 | export const ErrorAction = createAsyncComponent(() => import('./ErrorAction.vue')); 13 | 14 | export { FullScreen }; 15 | -------------------------------------------------------------------------------- /src/components/Application/src/useAppContext.ts: -------------------------------------------------------------------------------- 1 | import { InjectionKey, Ref } from 'vue'; 2 | import { createContext, useContext } from '/@/hooks/core/useContext'; 3 | 4 | export interface AppProviderContextProps { 5 | prefixCls: Ref; 6 | isMobile: Ref; 7 | } 8 | 9 | const key: InjectionKey = Symbol(); 10 | 11 | export function createAppProviderContext(context: AppProviderContextProps) { 12 | return createContext(context, key); 13 | } 14 | 15 | export function useAppProviderContext() { 16 | return useContext(key); 17 | } 18 | -------------------------------------------------------------------------------- /src/layouts/default/tabs/types.ts: -------------------------------------------------------------------------------- 1 | import type { DropMenu } from '/@/components/Dropdown/index'; 2 | import type { RouteLocationNormalized } from 'vue-router'; 3 | 4 | export enum TabContentEnum { 5 | TAB_TYPE, 6 | EXTRA_TYPE, 7 | } 8 | 9 | export type { DropMenu }; 10 | 11 | export interface TabContentProps { 12 | tabItem: RouteLocationNormalized; 13 | type?: TabContentEnum; 14 | trigger?: ('click' | 'hover' | 'contextmenu')[]; 15 | } 16 | 17 | export enum MenuEventEnum { 18 | REFRESH_PAGE, 19 | CLOSE_CURRENT, 20 | CLOSE_LEFT, 21 | CLOSE_RIGHT, 22 | CLOSE_OTHER, 23 | CLOSE_ALL, 24 | SCALE, 25 | } 26 | -------------------------------------------------------------------------------- /src/utils/dateUtil.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Independent time operation tool to facilitate subsequent switch to dayjs 3 | */ 4 | import moment from 'moment'; 5 | 6 | const DATE_TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss'; 7 | const DATE_FORMAT = 'YYYY-MM-DD '; 8 | 9 | export function formatToDateTime( 10 | date: moment.MomentInput = undefined, 11 | format = DATE_TIME_FORMAT, 12 | ): string { 13 | return moment(date).format(format); 14 | } 15 | 16 | export function formatToDate(date: moment.MomentInput = undefined, format = DATE_FORMAT): string { 17 | return moment(date).format(format); 18 | } 19 | 20 | export const dateUtil = moment; 21 | -------------------------------------------------------------------------------- /dist/assets/SiderTrigger.9c9ec3fc.js: -------------------------------------------------------------------------------- 1 | import{A as i,ce as d,cf as p,a0 as n,B as t,D as u,a1 as s,ax as c}from"./vendor.a45ea33e.js";import{_ as g,q as f}from"./index.c19540a9.js";const m=i({name:"SiderTrigger",components:{DoubleRightOutlined:d,DoubleLeftOutlined:p},setup(){const{getCollapsed:e,toggleCollapsed:o}=f();return{getCollapsed:e,toggleCollapsed:o}}});function C(e,o,_,D,b,k){const l=n("DoubleRightOutlined"),a=n("DoubleLeftOutlined");return t(),u("div",{onClick:o[0]||(o[0]=c((...r)=>e.toggleCollapsed&&e.toggleCollapsed(...r),["stop"]))},[e.getCollapsed?(t(),s(l,{key:0})):(t(),s(a,{key:1}))])}var B=g(m,[["render",C]]);export{B as default}; 2 | -------------------------------------------------------------------------------- /src/hooks/web/useSortable.ts: -------------------------------------------------------------------------------- 1 | import { nextTick, unref } from 'vue'; 2 | import type { Ref } from 'vue'; 3 | import type { Options } from 'sortablejs'; 4 | 5 | export function useSortable(el: HTMLElement | Ref, options?: Options) { 6 | function initSortable() { 7 | nextTick(async () => { 8 | if (!el) return; 9 | 10 | const Sortable = (await import('sortablejs')).default; 11 | Sortable.create(unref(el), { 12 | animation: 500, 13 | delay: 400, 14 | delayOnTouchOnly: true, 15 | ...options, 16 | }); 17 | }); 18 | } 19 | 20 | return { initSortable }; 21 | } 22 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 21 | -------------------------------------------------------------------------------- /src/api/sys/upload.ts: -------------------------------------------------------------------------------- 1 | import { UploadApiResult } from './model/uploadModel'; 2 | import { defHttp } from '/@/utils/http/axios'; 3 | import { UploadFileParams } from '/#/axios'; 4 | import { useGlobSetting } from '/@/hooks/setting'; 5 | 6 | const { uploadUrl = '' } = useGlobSetting(); 7 | 8 | /** 9 | * @description: Upload interface 10 | */ 11 | export function uploadApi( 12 | params: UploadFileParams, 13 | onUploadProgress: (progressEvent: ProgressEvent) => void, 14 | ) { 15 | return defHttp.uploadFile( 16 | { 17 | url: uploadUrl, 18 | onUploadProgress, 19 | }, 20 | params, 21 | ); 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Menu/src/components/BasicMenuItem.vue: -------------------------------------------------------------------------------- 1 | 6 | 21 | -------------------------------------------------------------------------------- /src/components/Table/src/hooks/useLoading.ts: -------------------------------------------------------------------------------- 1 | import { ref, ComputedRef, unref, computed, watch } from 'vue'; 2 | import type { BasicTableProps } from '../types/table'; 3 | 4 | export function useLoading(props: ComputedRef) { 5 | const loadingRef = ref(unref(props).loading); 6 | 7 | watch( 8 | () => unref(props).loading, 9 | (loading) => { 10 | loadingRef.value = loading; 11 | }, 12 | ); 13 | 14 | const getLoading = computed(() => unref(loadingRef)); 15 | 16 | function setLoading(loading: boolean) { 17 | loadingRef.value = loading; 18 | } 19 | 20 | return { getLoading, setLoading }; 21 | } 22 | -------------------------------------------------------------------------------- /src/layouts/default/setting/components/index.ts: -------------------------------------------------------------------------------- 1 | import { createAsyncComponent } from '/@/utils/factory/createAsyncComponent'; 2 | 3 | export const TypePicker = createAsyncComponent(() => import('./TypePicker.vue')); 4 | export const ThemeColorPicker = createAsyncComponent(() => import('./ThemeColorPicker.vue')); 5 | export const SettingFooter = createAsyncComponent(() => import('./SettingFooter.vue')); 6 | export const SwitchItem = createAsyncComponent(() => import('./SwitchItem.vue')); 7 | export const SelectItem = createAsyncComponent(() => import('./SelectItem.vue')); 8 | export const InputNumberItem = createAsyncComponent(() => import('./InputNumberItem.vue')); 9 | -------------------------------------------------------------------------------- /src/components/Table/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BasicTable } from './src/BasicTable.vue'; 2 | export { default as TableAction } from './src/components/TableAction.vue'; 3 | export { default as EditTableHeaderIcon } from './src/components/EditTableHeaderIcon.vue'; 4 | export { default as TableImg } from './src/components/TableImg.vue'; 5 | export * from './src/types/table'; 6 | export * from './src/types/pagination'; 7 | export * from './src/types/tableAction'; 8 | export { useTable } from './src/hooks/useTable'; 9 | export type { FormSchema, FormProps } from '/@/components/Form/src/types/form'; 10 | export type { EditRecordRow } from './src/components/editable'; 11 | -------------------------------------------------------------------------------- /src/layouts/default/content/useContentContext.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey, ComputedRef } from 'vue'; 2 | import { createContext, useContext } from '/@/hooks/core/useContext'; 3 | 4 | export interface ContentContextProps { 5 | contentHeight: ComputedRef; 6 | setPageHeight: (height: number) => Promise; 7 | } 8 | 9 | const key: InjectionKey = Symbol(); 10 | 11 | export function createContentContext(context: ContentContextProps) { 12 | return createContext(context, key, { native: true }); 13 | } 14 | 15 | export function useContentContext() { 16 | return useContext(key); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Modal/src/components/ModalHeader.vue: -------------------------------------------------------------------------------- 1 | 6 | 23 | -------------------------------------------------------------------------------- /src/hooks/component/usePageContext.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey, ComputedRef, Ref } from 'vue'; 2 | import { createContext, useContext } from '/@/hooks/core/useContext'; 3 | 4 | export interface PageContextProps { 5 | contentHeight: ComputedRef; 6 | pageHeight: Ref; 7 | setPageHeight: (height: number) => Promise; 8 | } 9 | 10 | const key: InjectionKey = Symbol(); 11 | 12 | export function createPageContext(context: PageContextProps) { 13 | return createContext(context, key, { native: true }); 14 | } 15 | 16 | export function usePageContext() { 17 | return useContext(key); 18 | } 19 | -------------------------------------------------------------------------------- /dist/assets/DetailModal.e31f13ac.js: -------------------------------------------------------------------------------- 1 | import{B as r}from"./index.0d1eabd6.js";import{u as i,D as n}from"./index.43de9b5f.js";import{b as c}from"./index.c19540a9.js";import{getDescSchema as d}from"./data.2665c166.js";import{A as m,B as p,a1 as f,a5 as u,w as l,u as e,a4 as j}from"./vendor.a45ea33e.js";import"./useWindowSizeFn.48fcda03.js";/* empty css */const k=m({props:{info:{type:Object,default:null}},setup(t){const{t:s}=c(),[a]=i({column:2,schema:d()});return(o,g)=>(p(),f(e(r),j({width:800,title:e(s)("sys.errorLog.tableActionDesc")},o.$attrs),{default:u(()=>[l(e(n),{data:t.info,onRegister:e(a)},null,8,["data","onRegister"])]),_:1},16,["title"]))}});export{k as default}; 2 | -------------------------------------------------------------------------------- /src/router/constant.ts: -------------------------------------------------------------------------------- 1 | export const REDIRECT_NAME = 'Redirect'; 2 | 3 | export const PARENT_LAYOUT_NAME = 'ParentLayout'; 4 | 5 | export const PAGE_NOT_FOUND_NAME = 'PageNotFound'; 6 | 7 | export const EXCEPTION_COMPONENT = () => import('/@/views/sys/exception/Exception.vue'); 8 | 9 | /** 10 | * @description: default layout 11 | */ 12 | export const LAYOUT = () => import('/@/layouts/default/index.vue'); 13 | 14 | /** 15 | * @description: parent-layout 16 | */ 17 | export const getParentLayout = (_name?: string) => { 18 | return () => 19 | new Promise((resolve) => { 20 | resolve({ 21 | name: PARENT_LAYOUT_NAME, 22 | }); 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/3-bug-cn.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🐛 Bug 报告 3 | about: 向我们报告一个Bug以帮助我们改进 4 | title: '' 5 | labels: 'bug: pending triage' 6 | assignees: '' 7 | --- 8 | 9 | **⚠️ 重要 ⚠️ 在进一步操作之前,请检查下列选项。如果您忽视此模板或者没有提供关键信息,您的 Issue 将直接被关闭** 10 | 11 | - [ ] 已阅读 [文档](https://anncwb.github.io/vue-vben-admin-doc/). 12 | - [ ] 确保您的代码已是最新或者所报告的 Bug 在最新版本中可以重现. (部分 Bug 可能已经在最近的代码中修复) 13 | - [ ] 已在 Issues 中搜索了相关的关键词 14 | - [ ] 不是 ant design vue 组件库的 Bug 15 | 16 | ### 描述 Bug 17 | 18 | 请清晰地描述此 Bug 的具体表现。 19 | 20 | ### 复现 Bug 21 | 22 | 请描述在演示页面中复现 Bug 的详细步骤,以确保我们可以理解并定位问题。部分 Bug 如果未在 Demo 中涉及,请务必提供关键代码 23 | 24 | ## 系统信息 25 | 26 | - 操作系统: 27 | - Node 版本: 28 | - 包管理器 (npm/yarn/pnpm) 及其版本: 29 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # Whether to open mock 2 | VITE_USE_MOCK = false 3 | 4 | # public path 5 | VITE_PUBLIC_PATH = / 6 | 7 | # Cross-domain proxy, you can configure multiple 8 | # Please note that no line breaks 9 | # VITE_PROXY = [["/basic-api","http://localhost:3000/api"],["/upload","http://localhost:3300/upload"],["/api","http://localhost:3000/api"]] 10 | # VITE_PROXY=[["/api","https://vvbin.cn/test"]] 11 | 12 | # Delete console 13 | VITE_DROP_CONSOLE = false 14 | 15 | # Basic interface address SPA 16 | VITE_GLOB_API_URL=http://localhost:3000/api 17 | 18 | # File upload address, optional 19 | VITE_GLOB_UPLOAD_URL=/upload 20 | 21 | # Interface prefix 22 | VITE_GLOB_API_URL_PREFIX= 23 | -------------------------------------------------------------------------------- /src/enums/breakpointEnum.ts: -------------------------------------------------------------------------------- 1 | export enum sizeEnum { 2 | XS = 'XS', 3 | SM = 'SM', 4 | MD = 'MD', 5 | LG = 'LG', 6 | XL = 'XL', 7 | XXL = 'XXL', 8 | } 9 | 10 | export enum screenEnum { 11 | XS = 480, 12 | SM = 576, 13 | MD = 768, 14 | LG = 992, 15 | XL = 1200, 16 | XXL = 1600, 17 | } 18 | 19 | const screenMap = new Map(); 20 | 21 | screenMap.set(sizeEnum.XS, screenEnum.XS); 22 | screenMap.set(sizeEnum.SM, screenEnum.SM); 23 | screenMap.set(sizeEnum.MD, screenEnum.MD); 24 | screenMap.set(sizeEnum.LG, screenEnum.LG); 25 | screenMap.set(sizeEnum.XL, screenEnum.XL); 26 | screenMap.set(sizeEnum.XXL, screenEnum.XXL); 27 | 28 | export { screenMap }; 29 | -------------------------------------------------------------------------------- /src/views/dashboard/workbench/components/QuickNav.vue: -------------------------------------------------------------------------------- 1 | 13 | 20 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | 8 | jobs: 9 | build: 10 | name: Create Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout code 14 | uses: actions/checkout@master 15 | 16 | - name: Create Release for Tag 17 | id: release_tag 18 | uses: yyx990803/release-tag@master 19 | env: 20 | GITHUB_TOKEN: ${{ secrets.OPER_TOKEN }} 21 | with: 22 | tag_name: ${{ github.ref }} 23 | body: | 24 | Please refer to [CHANGELOG.md](https://github.com/anncwb/vue-vben-admin/blob/main/CHANGELOG.md) for details. 25 | -------------------------------------------------------------------------------- /dist/resource/tinymce/skins/ui/oxide/content.mobile.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;} 8 | 9 | body{-webkit-text-size-adjust: none;} 10 | 11 | body img{max-width: 96vw;} 12 | 13 | body table img{max-width: 95%;} 14 | 15 | body{font-family: sans-serif;} 16 | 17 | table{border-collapse: collapse;} 18 | -------------------------------------------------------------------------------- /public/resource/tinymce/skins/ui/oxide/content.mobile.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;} 8 | 9 | body{-webkit-text-size-adjust: none;} 10 | 11 | body img{max-width: 96vw;} 12 | 13 | body table img{max-width: 95%;} 14 | 15 | body{font-family: sans-serif;} 16 | 17 | table{border-collapse: collapse;} 18 | -------------------------------------------------------------------------------- /dist/assets/download.29a0bc39.js: -------------------------------------------------------------------------------- 1 | import{o as s}from"./index.c19540a9.js";function c({url:e,target:o="_blank",fileName:r}){const i=window.navigator.userAgent.toLowerCase().indexOf("chrome")>-1,a=window.navigator.userAgent.toLowerCase().indexOf("safari")>-1;if(/(iP)/g.test(window.navigator.userAgent))return console.error("Your browser does not support download!"),!1;if(i||a){const n=document.createElement("a");if(n.href=e,n.target=o,n.download!==void 0&&(n.download=r||e.substring(e.lastIndexOf("/")+1,e.length)),document.createEvent){const t=document.createEvent("MouseEvents");return t.initEvent("click",!0,!0),n.dispatchEvent(t),!0}}return e.indexOf("?")===-1&&(e+="?download"),s(e,{target:o}),!0}export{c as d}; 2 | -------------------------------------------------------------------------------- /dist/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;} 8 | 9 | body{-webkit-text-size-adjust: none;} 10 | 11 | body img{max-width: 96vw;} 12 | 13 | body table img{max-width: 95%;} 14 | 15 | body{font-family: sans-serif;} 16 | 17 | table{border-collapse: collapse;} 18 | -------------------------------------------------------------------------------- /public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Tiny Technologies, Inc. All rights reserved. 3 | * Licensed under the LGPL or a commercial license. 4 | * For LGPL see License.txt in the project root for license information. 5 | * For commercial licenses see https://www.tiny.cloud/ 6 | */ 7 | .tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{position: absolute;display: inline-block;background-color: green;opacity: .5;} 8 | 9 | body{-webkit-text-size-adjust: none;} 10 | 11 | body img{max-width: 96vw;} 12 | 13 | body table img{max-width: 95%;} 14 | 15 | body{font-family: sans-serif;} 16 | 17 | table{border-collapse: collapse;} 18 | -------------------------------------------------------------------------------- /src/components/Excel/src/typing.ts: -------------------------------------------------------------------------------- 1 | import type { JSON2SheetOpts, WritingOptions, BookType } from 'xlsx'; 2 | 3 | export interface ExcelData { 4 | header: string[]; 5 | results: T[]; 6 | meta: { sheetName: string }; 7 | } 8 | 9 | export interface JsonToSheet { 10 | data: T[]; 11 | header?: T; 12 | filename?: string; 13 | json2sheetOpts?: JSON2SheetOpts; 14 | write2excelOpts?: WritingOptions; 15 | } 16 | 17 | export interface AoAToSheet { 18 | data: T[][]; 19 | header?: T[]; 20 | filename?: string; 21 | write2excelOpts?: WritingOptions; 22 | } 23 | 24 | export interface ExportModalResult { 25 | filename: string; 26 | bookType: BookType; 27 | } 28 | -------------------------------------------------------------------------------- /src/components/Table/src/components/ExpandIcon.tsx: -------------------------------------------------------------------------------- 1 | import { BasicArrow } from '/@/components/Basic'; 2 | 3 | export default () => { 4 | return (props: Recordable) => { 5 | if (!props.expandable) { 6 | if (props.needIndentSpaced) { 7 | return ; 8 | } else { 9 | return ; 10 | } 11 | } 12 | return ( 13 | { 17 | props.onExpand(props.record, e); 18 | }} 19 | expand={props.expanded} 20 | /> 21 | ); 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /dist/assets/index.c483c751.js: -------------------------------------------------------------------------------- 1 | import{G as c,b as d,c as p,F as l}from"./index.809887c2.js";import{A as m,r as t,B as u,D as x,w as n,u as o,aa as g}from"./vendor.a45ea33e.js";import"./index.c19540a9.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */const k=m({setup(j){const r=t([]),a=t(),i=e=>{r.value=e,console.log(r.value)},s=e=>{a.value=e,console.log(a.value)};return(e,f)=>(u(),x(g,null,[n(o(c),{onChangeGidList:i}),n(o(d),{onChangeGroupUserId:s}),n(o(p),{onChangeGroupId:s}),n(o(l),{onChangeUserId:s})],64))}});export{k as default}; 2 | -------------------------------------------------------------------------------- /dist/assets/index.e7586e6f.css: -------------------------------------------------------------------------------- 1 | .vben-header-user-dropdown{height:48px;padding:0 10px;overflow:hidden;font-size:12px;cursor:pointer;align-items:center}.vben-header-user-dropdown img{width:24px;height:24px;margin-right:12px}.vben-header-user-dropdown__header{border-radius:50%}.vben-header-user-dropdown__name{font-size:14px}.vben-header-user-dropdown--dark:hover{background-color:var(--header-bg-hover-color)}.vben-header-user-dropdown--light:hover{background-color:#f6f6f6}.vben-header-user-dropdown--light .vben-header-user-dropdown__name{color:#000000d9}.vben-header-user-dropdown--light .vben-header-user-dropdown__desc{color:#7c8087}.vben-header-user-dropdown-dropdown-overlay .ant-dropdown-menu-item{min-width:160px} 2 | -------------------------------------------------------------------------------- /src/components/Markdown/src/MarkdownViewer.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 18 | 19 | 24 | -------------------------------------------------------------------------------- /src/enums/httpEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: Request result set 3 | */ 4 | export enum ResultEnum { 5 | SUCCESS = 0, 6 | ERROR = 1, 7 | TIMEOUT = 401, 8 | TYPE = 'success', 9 | } 10 | 11 | /** 12 | * @description: request method 13 | */ 14 | export enum RequestEnum { 15 | GET = 'GET', 16 | POST = 'POST', 17 | PUT = 'PUT', 18 | DELETE = 'DELETE', 19 | } 20 | 21 | /** 22 | * @description: contentTyp 23 | */ 24 | export enum ContentTypeEnum { 25 | // json 26 | JSON = 'application/json;charset=UTF-8', 27 | // form-data qs 28 | FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8', 29 | // form-data upload 30 | FORM_DATA = 'multipart/form-data;charset=UTF-8', 31 | } 32 | -------------------------------------------------------------------------------- /types/module.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.vue' { 2 | import { DefineComponent } from 'vue'; 3 | const Component: DefineComponent<{}, {}, any>; 4 | export default Component; 5 | } 6 | 7 | declare module 'ant-design-vue/es/locale/*' { 8 | import { Locale } from 'ant-design-vue/types/locale-provider'; 9 | const locale: Locale & ReadonlyRecordable; 10 | export default locale as Locale & ReadonlyRecordable; 11 | } 12 | 13 | declare module 'moment/dist/locale/*' { 14 | import { LocaleSpecification } from 'moment'; 15 | const locale: LocaleSpecification & ReadonlyRecordable; 16 | export default locale; 17 | } 18 | 19 | declare module 'virtual:*' { 20 | const result: any; 21 | export default result; 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Application/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | 3 | import appLogo from './src/AppLogo.vue'; 4 | import appProvider from './src/AppProvider.vue'; 5 | import appSearch from './src/search/AppSearch.vue'; 6 | import appLocalePicker from './src/AppLocalePicker.vue'; 7 | import appDarkModeToggle from './src/AppDarkModeToggle.vue'; 8 | 9 | export { useAppProviderContext } from './src/useAppContext'; 10 | 11 | export const AppLogo = withInstall(appLogo); 12 | export const AppProvider = withInstall(appProvider); 13 | export const AppSearch = withInstall(appSearch); 14 | export const AppLocalePicker = withInstall(appLocalePicker); 15 | export const AppDarkModeToggle = withInstall(appDarkModeToggle); 16 | -------------------------------------------------------------------------------- /dist/assets/QuickNav.6011eb09.js: -------------------------------------------------------------------------------- 1 | import{A as d,bL as t,B as s,a1 as o,a5 as r,D as l,ao as p,u as a,H as n,w as u,J as m,aa as f,a4 as _}from"./vendor.a45ea33e.js";/* empty css *//* empty css *//* empty css */import{n as x}from"./data.8ddc77c3.js";import{I as B}from"./index.c19540a9.js";const j={class:"flex flex-col items-center"},C={class:"text-md mt-2"},A=d({setup(h){const c=t.Grid;return(i,k)=>(s(),o(a(t),_({title:"\u5FEB\u6377\u5BFC\u822A"},i.$attrs),{default:r(()=>[(s(!0),l(f,null,p(a(x),e=>(s(),o(a(c),{key:e},{default:r(()=>[n("span",j,[u(a(B),{icon:e.icon,color:e.color,size:"20"},null,8,["icon","color"]),n("span",C,m(e.title),1)])]),_:2},1024))),128))]),_:1},16))}});export{A as default}; 2 | -------------------------------------------------------------------------------- /dist/assets/DropMenuItem.7d534f1b.js: -------------------------------------------------------------------------------- 1 | import{A as i,az as u,j as d,a0 as a,B as l,a1 as _,a5 as I,H as r,w as f,J as y,ah as k}from"./vendor.a45ea33e.js";import{_ as M,I as g,p as s}from"./index.c19540a9.js";const x=i({name:"DropdownMenuItem",components:{MenuItem:u.Item,Icon:g},props:{key:s.string,text:s.string,icon:s.string},setup(e){const n=k();return{itemKey:d(()=>{var t,o;return e.key||((o=(t=n==null?void 0:n.vnode)==null?void 0:t.props)==null?void 0:o.key)})}}}),h={class:"flex items-center"};function v(e,n,c,t,o,B){const p=a("Icon"),m=a("MenuItem");return l(),_(m,{key:e.itemKey},{default:I(()=>[r("span",h,[f(p,{icon:e.icon,class:"mr-1"},null,8,["icon"]),r("span",null,y(e.text),1)])]),_:1})}var j=M(x,[["render",v]]);export{j as default}; 2 | -------------------------------------------------------------------------------- /src/components/Form/index.ts: -------------------------------------------------------------------------------- 1 | import BasicForm from './src/BasicForm.vue'; 2 | 3 | export * from './src/types/form'; 4 | export * from './src/types/formItem'; 5 | 6 | export { useComponentRegister } from './src/hooks/useComponentRegister'; 7 | export { useForm } from './src/hooks/useForm'; 8 | 9 | export { default as ApiSelect } from './src/components/ApiSelect.vue'; 10 | export { default as RadioButtonGroup } from './src/components/RadioButtonGroup.vue'; 11 | export { default as ApiTreeSelect } from './src/components/ApiTreeSelect.vue'; 12 | export { default as ApiRadioGroup } from './src/components/ApiRadioGroup.vue'; 13 | export { default as ApiCascader } from './src/components/ApiCascader.vue'; 14 | 15 | export { BasicForm }; 16 | -------------------------------------------------------------------------------- /src/components/Table/src/hooks/useTableContext.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue'; 2 | import type { BasicTableProps, TableActionType } from '../types/table'; 3 | import { provide, inject, ComputedRef } from 'vue'; 4 | 5 | const key = Symbol('basic-table'); 6 | 7 | type Instance = TableActionType & { 8 | wrapRef: Ref>; 9 | getBindValues: ComputedRef; 10 | }; 11 | 12 | type RetInstance = Omit & { 13 | getBindValues: ComputedRef; 14 | }; 15 | 16 | export function createTableContext(instance: Instance) { 17 | provide(key, instance); 18 | } 19 | 20 | export function useTableContext(): RetInstance { 21 | return inject(key) as RetInstance; 22 | } 23 | -------------------------------------------------------------------------------- /src/components/Preview/src/functional.ts: -------------------------------------------------------------------------------- 1 | import type { Options, Props } from './typing'; 2 | import ImgPreview from './Functional.vue'; 3 | import { isClient } from '/@/utils/is'; 4 | import { createVNode, render } from 'vue'; 5 | 6 | let instance: ReturnType | null = null; 7 | export function createImgPreview(options: Options) { 8 | if (!isClient) return; 9 | const propsData: Partial = {}; 10 | const container = document.createElement('div'); 11 | Object.assign(propsData, { show: true, index: 0, scaleStep: 100 }, options); 12 | 13 | instance = createVNode(ImgPreview, propsData); 14 | render(instance, container); 15 | document.body.appendChild(container); 16 | return instance.component?.exposed; 17 | } 18 | -------------------------------------------------------------------------------- /src/design/var/index.less: -------------------------------------------------------------------------------- 1 | @import (reference) '../color.less'; 2 | @import 'easing'; 3 | @import 'breakpoint'; 4 | 5 | @namespace: vben; 6 | 7 | // tabs 8 | @multiple-height: 30px; 9 | 10 | // headers 11 | @header-height: 48px; 12 | 13 | // logo width 14 | @logo-width: 32px; 15 | 16 | // 17 | @side-drag-z-index: 200; 18 | 19 | @page-loading-z-index: 10000; 20 | 21 | @lock-page-z-index: 3000; 22 | 23 | @layout-header-fixed-z-index: 500; 24 | 25 | @multiple-tab-fixed-z-index: 505; 26 | 27 | @layout-sider-fixed-z-index: 510; 28 | 29 | @layout-mix-sider-fixed-z-index: 550; 30 | 31 | @preview-comp-z-index: 1000; 32 | 33 | @page-footer-z-index: 99; 34 | 35 | .bem(@n; @content) { 36 | @{namespace}-@{n} { 37 | @content(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/layouts/default/setting/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 27 | -------------------------------------------------------------------------------- /src/settings/localeSetting.ts: -------------------------------------------------------------------------------- 1 | import type { DropMenu } from '../components/Dropdown'; 2 | import type { LocaleSetting, LocaleType } from '/#/config'; 3 | 4 | export const LOCALE: { [key: string]: LocaleType } = { 5 | ZH_CN: 'zh_CN', 6 | EN_US: 'en', 7 | }; 8 | 9 | export const localeSetting: LocaleSetting = { 10 | showPicker: true, 11 | // Locale 12 | locale: LOCALE.ZH_CN, 13 | // Default locale 14 | fallback: LOCALE.ZH_CN, 15 | // available Locales 16 | availableLocales: [LOCALE.ZH_CN, LOCALE.EN_US], 17 | }; 18 | 19 | // locale list 20 | export const localeList: DropMenu[] = [ 21 | { 22 | text: '简体中文', 23 | event: LOCALE.ZH_CN, 24 | }, 25 | { 26 | text: 'English', 27 | event: LOCALE.EN_US, 28 | }, 29 | ]; 30 | -------------------------------------------------------------------------------- /dist/assets/Breadcrumb.45a81991.css: -------------------------------------------------------------------------------- 1 | .vben-layout-breadcrumb{display:flex;padding:0 8px;align-items:center}.vben-layout-breadcrumb .ant-breadcrumb-link .anticon{margin-right:4px;margin-bottom:2px}.vben-layout-breadcrumb--light .ant-breadcrumb-link{color:#999}.vben-layout-breadcrumb--light .ant-breadcrumb-link a{color:#000000a6}.vben-layout-breadcrumb--light .ant-breadcrumb-link a:hover{color:#0960bd}.vben-layout-breadcrumb--light .ant-breadcrumb-separator{color:#999}.vben-layout-breadcrumb--dark .ant-breadcrumb-link{color:#fff9}.vben-layout-breadcrumb--dark .ant-breadcrumb-link a{color:#fffc}.vben-layout-breadcrumb--dark .ant-breadcrumb-link a:hover{color:#fff}.vben-layout-breadcrumb--dark .ant-breadcrumb-separator,.vben-layout-breadcrumb--dark .anticon{color:#fffc} 2 | -------------------------------------------------------------------------------- /src/components/SimpleMenu/src/components/useSimpleMenuContext.ts: -------------------------------------------------------------------------------- 1 | import type { InjectionKey, Ref } from 'vue'; 2 | import type { Emitter } from '/@/utils/mitt'; 3 | import { createContext, useContext } from '/@/hooks/core/useContext'; 4 | 5 | export interface SimpleRootMenuContextProps { 6 | rootMenuEmitter: Emitter; 7 | activeName: Ref; 8 | } 9 | 10 | const key: InjectionKey = Symbol(); 11 | 12 | export function createSimpleRootMenuContext(context: SimpleRootMenuContextProps) { 13 | return createContext(context, key, { readonly: false, native: true }); 14 | } 15 | 16 | export function useSimpleRootMenuContext() { 17 | return useContext(key); 18 | } 19 | -------------------------------------------------------------------------------- /src/layouts/default/trigger/SiderTrigger.vue: -------------------------------------------------------------------------------- 1 | 7 | 22 | -------------------------------------------------------------------------------- /src/design/transition/slide.less: -------------------------------------------------------------------------------- 1 | .slide-y-transition { 2 | .transition-default(); 3 | 4 | &-enter-from, 5 | &-leave-to { 6 | opacity: 0%; 7 | transform: translateY(-15px); 8 | } 9 | } 10 | 11 | .slide-y-reverse-transition { 12 | .transition-default(); 13 | 14 | &-enter-from, 15 | &-leave-to { 16 | opacity: 0%; 17 | transform: translateY(15px); 18 | } 19 | } 20 | 21 | .slide-x-transition { 22 | .transition-default(); 23 | 24 | &-enter-from, 25 | &-leave-to { 26 | opacity: 0%; 27 | transform: translateX(-15px); 28 | } 29 | } 30 | 31 | .slide-x-reverse-transition { 32 | .transition-default(); 33 | 34 | &-enter-from, 35 | &-leave-to { 36 | opacity: 0%; 37 | transform: translateX(15px); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/components/Upload/src/ThumbUrl.vue: -------------------------------------------------------------------------------- 1 | 6 | 19 | 30 | -------------------------------------------------------------------------------- /src/layouts/default/trigger/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 23 | -------------------------------------------------------------------------------- /.yarnclean: -------------------------------------------------------------------------------- 1 | # test directories 2 | __tests__ 3 | test 4 | tests 5 | powered-test 6 | 7 | # asset directories 8 | docs 9 | doc 10 | website 11 | images 12 | assets 13 | 14 | # examples 15 | example 16 | examples 17 | 18 | # code coverage directories 19 | coverage 20 | .nyc_output 21 | 22 | # build scripts 23 | Makefile 24 | Gulpfile.js 25 | Gruntfile.js 26 | 27 | # configs 28 | appveyor.yml 29 | circle.yml 30 | codeship-services.yml 31 | codeship-steps.yml 32 | wercker.yml 33 | .tern-project 34 | .gitattributes 35 | .editorconfig 36 | .*ignore 37 | .eslintrc 38 | .jshintrc 39 | .flowconfig 40 | .documentup.json 41 | .yarn-metadata.json 42 | .travis.yml 43 | 44 | # misc 45 | *.md 46 | 47 | !istanbul-reports/lib/html/assets 48 | !istanbul-api/node_modules/istanbul-reports/lib/html/assets 49 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | declare interface Fn { 2 | (...arg: T[]): R; 3 | } 4 | 5 | declare interface PromiseFn { 6 | (...arg: T[]): Promise; 7 | } 8 | 9 | declare type RefType = T | null; 10 | 11 | declare type LabelValueOptions = { 12 | label: string; 13 | value: any; 14 | [key: string]: string | number | boolean; 15 | }[]; 16 | 17 | declare type EmitType = (event: string, ...args: any[]) => void; 18 | 19 | declare type TargetContext = '_self' | '_blank'; 20 | 21 | declare interface ComponentElRef { 22 | $el: T; 23 | } 24 | 25 | declare type ComponentRef = ComponentElRef | null; 26 | 27 | declare type ElRef = Nullable; 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/2-feature.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 🚀 Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | 17 | 18 | ### Subject of the feature 19 | 20 | Describe your issue here. 21 | 22 | ### Problem 23 | 24 | If the feature requests relates to a problem, please describe the problem you are trying to solve here. 25 | 26 | ### Expected behaviour 27 | 28 | What should happen? Please describe the desired behaviour. 29 | 30 | ### Alternatives 31 | 32 | What are the alternative solutions? Please describe what else you have considered? 33 | -------------------------------------------------------------------------------- /src/api/sys/model/userModel.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: Login interface parameters 3 | */ 4 | export interface LoginParams { 5 | username: string; 6 | password: string; 7 | } 8 | 9 | export interface RoleInfo { 10 | roleName: string; 11 | value: string; 12 | } 13 | 14 | /** 15 | * @description: Login interface return value 16 | */ 17 | export interface LoginResultModel { 18 | userId: string | number; 19 | token: string; 20 | role: RoleInfo; 21 | } 22 | 23 | /** 24 | * @description: Get user information return value 25 | */ 26 | export interface GetUserInfoModel { 27 | roles: RoleInfo[]; 28 | // 用户id 29 | userId: string | number; 30 | // 用户名 31 | username: string; 32 | // 真实名字 33 | realName: string; 34 | // 头像 35 | avatar: string; 36 | // 介绍 37 | desc?: string; 38 | } 39 | -------------------------------------------------------------------------------- /src/components/SimpleMenu/src/components/types.ts: -------------------------------------------------------------------------------- 1 | import { Ref } from 'vue'; 2 | 3 | export interface Props { 4 | theme: string; 5 | activeName?: string | number | undefined; 6 | openNames: string[]; 7 | accordion: boolean; 8 | width: string; 9 | collapsedWidth: string; 10 | indentSize: number; 11 | collapse: boolean; 12 | activeSubMenuNames: (string | number)[]; 13 | } 14 | 15 | export interface SubMenuProvider { 16 | addSubMenu: (name: string | number, update?: boolean) => void; 17 | removeSubMenu: (name: string | number, update?: boolean) => void; 18 | removeAll: () => void; 19 | sliceIndex: (index: number) => void; 20 | isRemoveAllPopup: Ref; 21 | getOpenNames: () => (string | number)[]; 22 | handleMouseleave?: Fn; 23 | level: number; 24 | props: Props; 25 | } 26 | -------------------------------------------------------------------------------- /src/views/sys/redirect/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 31 | -------------------------------------------------------------------------------- /dist/assets/SiteAnalysis.60a5a3b3.js: -------------------------------------------------------------------------------- 1 | import{A as c,r as u,B as t,a1 as m,a5 as p,D as s,w as o,ac as r,a4 as b,u as d,bL as f}from"./vendor.a45ea33e.js";/* empty css *//* empty css *//* empty css */import l from"./VisitAnalysis.ad7c818e.js";import _ from"./VisitAnalysisBar.5e71d8fc.js";import"./useECharts.9a72bc67.js";import"./index.c19540a9.js";import"./props.f48aca0b.js";const j={key:0},k={key:1},$=c({setup(y){const a=u("tab1"),i=[{key:"tab1",tab:"\u6D41\u91CF\u8D8B\u52BF"},{key:"tab2",tab:"\u8BBF\u95EE\u91CF"}];function n(e){a.value=e}return(e,B)=>(t(),m(d(f),b({"tab-list":i},e.$attrs,{"active-tab-key":a.value,onTabChange:n}),{default:p(()=>[a.value==="tab1"?(t(),s("p",j,[o(l)])):r("",!0),a.value==="tab2"?(t(),s("p",k,[o(_)])):r("",!0)]),_:1},16,["active-tab-key"]))}});export{$ as default}; 2 | -------------------------------------------------------------------------------- /src/components/ContextMenu/src/typing.ts: -------------------------------------------------------------------------------- 1 | export interface Axis { 2 | x: number; 3 | y: number; 4 | } 5 | 6 | export interface ContextMenuItem { 7 | label: string; 8 | icon?: string; 9 | disabled?: boolean; 10 | handler?: Fn; 11 | divider?: boolean; 12 | children?: ContextMenuItem[]; 13 | } 14 | export interface CreateContextOptions { 15 | event: MouseEvent; 16 | icon?: string; 17 | styles?: any; 18 | items?: ContextMenuItem[]; 19 | } 20 | 21 | export interface ContextMenuProps { 22 | event?: MouseEvent; 23 | styles?: any; 24 | items: ContextMenuItem[]; 25 | customEvent?: MouseEvent; 26 | axis?: Axis; 27 | width?: number; 28 | showIcon?: boolean; 29 | } 30 | 31 | export interface ItemContentProps { 32 | showIcon: boolean | undefined; 33 | item: ContextMenuItem; 34 | handler: Fn; 35 | } 36 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | ignores: [(commit) => commit.includes('init')], 3 | extends: ['@commitlint/config-conventional'], 4 | rules: { 5 | 'body-leading-blank': [2, 'always'], 6 | 'footer-leading-blank': [1, 'always'], 7 | 'header-max-length': [2, 'always', 108], 8 | 'subject-empty': [2, 'never'], 9 | 'type-empty': [2, 'never'], 10 | 'subject-case': [0], 11 | 'type-enum': [ 12 | 2, 13 | 'always', 14 | [ 15 | 'feat', 16 | 'fix', 17 | 'perf', 18 | 'style', 19 | 'docs', 20 | 'test', 21 | 'refactor', 22 | 'build', 23 | 'ci', 24 | 'chore', 25 | 'revert', 26 | 'wip', 27 | 'workflow', 28 | 'types', 29 | 'release', 30 | ], 31 | ], 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /src/enums/cacheEnum.ts: -------------------------------------------------------------------------------- 1 | // token key 2 | export const TOKEN_KEY = 'TOKEN__'; 3 | 4 | export const LOCALE_KEY = 'LOCALE__'; 5 | 6 | // user info key 7 | export const USER_INFO_KEY = 'USER__INFO__'; 8 | 9 | // role info key 10 | export const ROLES_KEY = 'ROLES__KEY__'; 11 | 12 | // project config key 13 | export const PROJ_CFG_KEY = 'PROJ__CFG__KEY__'; 14 | 15 | // lock info 16 | export const LOCK_INFO_KEY = 'LOCK__INFO__KEY__'; 17 | 18 | export const MULTIPLE_TABS_KEY = 'MULTIPLE_TABS__KEY__'; 19 | 20 | export const APP_DARK_MODE_KEY_ = '__APP__DARK__MODE__'; 21 | 22 | // base global local key 23 | export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__'; 24 | 25 | // base global session key 26 | export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__'; 27 | 28 | export enum CacheTypeEnum { 29 | SESSION, 30 | LOCAL, 31 | } 32 | -------------------------------------------------------------------------------- /src/utils/uuid.ts: -------------------------------------------------------------------------------- 1 | const hexList: string[] = []; 2 | for (let i = 0; i <= 15; i++) { 3 | hexList[i] = i.toString(16); 4 | } 5 | 6 | export function buildUUID(): string { 7 | let uuid = ''; 8 | for (let i = 1; i <= 36; i++) { 9 | if (i === 9 || i === 14 || i === 19 || i === 24) { 10 | uuid += '-'; 11 | } else if (i === 15) { 12 | uuid += 4; 13 | } else if (i === 20) { 14 | uuid += hexList[(Math.random() * 4) | 8]; 15 | } else { 16 | uuid += hexList[(Math.random() * 16) | 0]; 17 | } 18 | } 19 | return uuid.replace(/-/g, ''); 20 | } 21 | 22 | let unique = 0; 23 | export function buildShortUUID(prefix = ''): string { 24 | const time = Date.now(); 25 | const random = Math.floor(Math.random() * 1000000000); 26 | unique++; 27 | return prefix + '_' + random + unique + String(time); 28 | } 29 | -------------------------------------------------------------------------------- /src/logics/theme/dark.ts: -------------------------------------------------------------------------------- 1 | import { darkCssIsReady, loadDarkThemeCss } from 'vite-plugin-theme/es/client'; 2 | import { addClass, hasClass, removeClass } from '/@/utils/domUtils'; 3 | 4 | export async function updateDarkTheme(mode: string | null = 'light') { 5 | const htmlRoot = document.getElementById('htmlRoot'); 6 | if (!htmlRoot) { 7 | return; 8 | } 9 | const hasDarkClass = hasClass(htmlRoot, 'dark'); 10 | if (mode === 'dark') { 11 | if (import.meta.env.PROD && !darkCssIsReady) { 12 | await loadDarkThemeCss(); 13 | } 14 | htmlRoot.setAttribute('data-theme', 'dark'); 15 | if (!hasDarkClass) { 16 | addClass(htmlRoot, 'dark'); 17 | } 18 | } else { 19 | htmlRoot.setAttribute('data-theme', 'light'); 20 | if (hasDarkClass) { 21 | removeClass(htmlRoot, 'dark'); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/views/bot/test/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /dist/assets/data.2665c166.js: -------------------------------------------------------------------------------- 1 | import{w as o,bs as n}from"./vendor.a45ea33e.js";/* empty css */import{b as d,aV as a}from"./index.c19540a9.js";const{t}=d();function l(){return[{dataIndex:"type",title:t("sys.errorLog.tableColumnType"),width:80,customRender:({text:e})=>{const r=e===a.VUE?"green":e===a.RESOURCE?"cyan":e===a.PROMISE?"blue":a.AJAX?"red":"purple";return o(n,{color:r},{default:()=>e})}},{dataIndex:"url",title:"URL",width:200},{dataIndex:"time",title:t("sys.errorLog.tableColumnDate"),width:160},{dataIndex:"file",title:t("sys.errorLog.tableColumnFile"),width:200},{dataIndex:"name",title:"Name",width:200},{dataIndex:"message",title:t("sys.errorLog.tableColumnMsg"),width:300},{dataIndex:"stack",title:t("sys.errorLog.tableColumnStackMsg")}]}function u(){return l().map(e=>({field:e.dataIndex,label:e.title}))}export{l as getColumns,u as getDescSchema}; 2 | -------------------------------------------------------------------------------- /src/components/Table/src/components/editable/helper.ts: -------------------------------------------------------------------------------- 1 | import { ComponentType } from '../../types/componentType'; 2 | import { useI18n } from '/@/hooks/web/useI18n'; 3 | 4 | const { t } = useI18n(); 5 | 6 | /** 7 | * @description: 生成placeholder 8 | */ 9 | export function createPlaceholderMessage(component: ComponentType) { 10 | if (component.includes('Input')) { 11 | return t('common.inputText'); 12 | } 13 | if (component.includes('Picker')) { 14 | return t('common.chooseText'); 15 | } 16 | 17 | if ( 18 | component.includes('Select') || 19 | component.includes('Checkbox') || 20 | component.includes('Radio') || 21 | component.includes('Switch') || 22 | component.includes('DatePicker') || 23 | component.includes('TimePicker') 24 | ) { 25 | return t('common.chooseText'); 26 | } 27 | return ''; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Table/src/types/tableAction.ts: -------------------------------------------------------------------------------- 1 | import { ButtonProps } from 'ant-design-vue/es/button/buttonTypes'; 2 | import { TooltipProps } from 'ant-design-vue/es/tooltip/Tooltip'; 3 | import { RoleEnum } from '/@/enums/roleEnum'; 4 | export interface ActionItem extends ButtonProps { 5 | onClick?: Fn; 6 | label?: string; 7 | color?: 'success' | 'error' | 'warning'; 8 | icon?: string; 9 | popConfirm?: PopConfirm; 10 | disabled?: boolean; 11 | divider?: boolean; 12 | // 权限编码控制是否显示 13 | auth?: RoleEnum | RoleEnum[] | string | string[]; 14 | // 业务控制是否显示 15 | ifShow?: boolean | ((action: ActionItem) => boolean); 16 | tooltip?: string | TooltipProps; 17 | } 18 | 19 | export interface PopConfirm { 20 | title: string; 21 | okText?: string; 22 | cancelText?: string; 23 | confirm: Fn; 24 | cancel?: Fn; 25 | icon?: string; 26 | } 27 | -------------------------------------------------------------------------------- /dist/assets/useContentViewHeight.4cbdde4a.js: -------------------------------------------------------------------------------- 1 | var h=(t,i,e)=>new Promise((a,o)=>{var H=n=>{try{u(e.next(n))}catch(s){o(s)}},d=n=>{try{u(e.throw(n))}catch(s){o(s)}},u=n=>n.done?a(n.value):Promise.resolve(n.value).then(H,d);u((e=e.apply(t,i)).next())});import{x as m}from"./index.c19540a9.js";import{u as w}from"./useWindowSizeFn.48fcda03.js";import{r,j as p,u as c}from"./vendor.a45ea33e.js";const v=Symbol();function l(t){return m(t,v,{native:!0})}const g=r(0),f=r(0);function F(){function t(e){g.value=e}function i(e){f.value=e}return{headerHeightRef:g,footerHeightRef:f,setHeaderHeight:t,setFooterHeight:i}}function S(){const t=r(window.innerHeight),i=r(window.innerHeight),e=p(()=>c(t)-c(g)-c(f)||0);w(()=>{t.value=window.innerHeight},100,{immediate:!0});function a(o){return h(this,null,function*(){i.value=o})}l({contentHeight:e,setPageHeight:a,pageHeight:i})}export{F as a,S as u}; 2 | -------------------------------------------------------------------------------- /src/components/Table/src/hooks/useTableStyle.ts: -------------------------------------------------------------------------------- 1 | import type { ComputedRef } from 'vue'; 2 | import type { BasicTableProps, TableCustomRecord } from '../types/table'; 3 | import { unref } from 'vue'; 4 | import { isFunction } from '/@/utils/is'; 5 | 6 | export function useTableStyle(propsRef: ComputedRef, prefixCls: string) { 7 | function getRowClassName(record: TableCustomRecord, index: number) { 8 | const { striped, rowClassName } = unref(propsRef); 9 | const classNames: string[] = []; 10 | if (striped) { 11 | classNames.push((index || 0) % 2 === 1 ? `${prefixCls}-row__striped` : ''); 12 | } 13 | if (rowClassName && isFunction(rowClassName)) { 14 | classNames.push(rowClassName(record, index)); 15 | } 16 | return classNames.filter((cls) => !!cls).join(' '); 17 | } 18 | 19 | return { getRowClassName }; 20 | } 21 | -------------------------------------------------------------------------------- /src/hooks/web/useDesign.ts: -------------------------------------------------------------------------------- 1 | import { useAppProviderContext } from '/@/components/Application'; 2 | // import { computed } from 'vue'; 3 | // import { lowerFirst } from 'lodash-es'; 4 | export function useDesign(scope: string) { 5 | const values = useAppProviderContext(); 6 | // const $style = cssModule ? useCssModule() : {}; 7 | 8 | // const style: Record = {}; 9 | // if (cssModule) { 10 | // Object.keys($style).forEach((key) => { 11 | // // const moduleCls = $style[key]; 12 | // const k = key.replace(new RegExp(`^${values.prefixCls}-?`, 'ig'), ''); 13 | // style[lowerFirst(k)] = $style[key]; 14 | // }); 15 | // } 16 | return { 17 | // prefixCls: computed(() => `${values.prefixCls}-${scope}`), 18 | prefixCls: `${values.prefixCls}-${scope}`, 19 | prefixVar: values.prefixCls, 20 | // style, 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /dist/assets/useSortable.a6e1258f.js: -------------------------------------------------------------------------------- 1 | var m=Object.defineProperty;var u=Object.getOwnPropertySymbols;var _=Object.prototype.hasOwnProperty,p=Object.prototype.propertyIsEnumerable;var f=(a,t,e)=>t in a?m(a,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):a[t]=e,l=(a,t)=>{for(var e in t||(t={}))_.call(t,e)&&f(a,e,t[e]);if(u)for(var e of u(t))p.call(t,e)&&f(a,e,t[e]);return a};var c=(a,t,e)=>new Promise((i,s)=>{var b=r=>{try{o(e.next(r))}catch(n){s(n)}},d=r=>{try{o(e.throw(r))}catch(n){s(n)}},o=r=>r.done?i(r.value):Promise.resolve(r.value).then(b,d);o((e=e.apply(a,t)).next())});import{F as y}from"./index.c19540a9.js";import{$ as S,u as j}from"./vendor.a45ea33e.js";function T(a,t){function e(){S(()=>c(this,null,function*(){if(!a)return;(yield y(()=>import("./sortable.esm.6bfbf233.js"),[])).default.create(j(a),l({animation:500,delay:400,delayOnTouchOnly:!0},t))}))}return{initSortable:e}}export{T as u}; 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

ZeroBot-Plugin Logo

2 | 3 | [![license](https://img.shields.io/github/license/FloatTech/ZeroBot-Plugin-Webui.svg)](LICENSE) 4 | 5 |

ZeroBot-Plugin-Webui

6 |
7 | 8 | ## 简介 9 | 10 | [ZeroBot-Plugin](https://github.com/FloatTech/ZeroBot-Plugin)的前端管理界面, 主要目的是实现插件控制的快速交互, 消息查看和处理, 后续也许会针对某些特殊插件, 比如 qq 空间表白墙插件写页面, 持续开发和优化中 11 | 12 | ZeroBot-Plugin 缺少前端管理, 写这个主要弥补 zbp 交互的不足, 同时也是为了提高自己前端的编码的能力, 最近才开始真正写前端, 之前看了很多前端相关知识, 看懂了但写不出来, 后面鼓起勇气开始一点一点编写前端, 才知道学代码需要多写, 单看是没有用的 13 | 14 | ## 本地启动和打包 15 | 16 | ``` 17 | pnpm run dev 18 | 19 | pnpm run build 20 | ``` 21 | 22 | ## 部署 23 | 24 | 已经将打包好的静态文件, 嵌入到 go 后端一起部署 25 | 26 | ## 抓壮丁 27 | 28 | 快来人写 webui, 小白也没关系, 手把手教学 29 | -------------------------------------------------------------------------------- /src/directives/permission.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Global authority directive 3 | * Used for fine-grained control of component permissions 4 | * @Example v-auth="RoleEnum.TEST" 5 | */ 6 | import type { App, Directive, DirectiveBinding } from 'vue'; 7 | 8 | import { usePermission } from '/@/hooks/web/usePermission'; 9 | 10 | function isAuth(el: Element, binding: any) { 11 | const { hasPermission } = usePermission(); 12 | 13 | const value = binding.value; 14 | if (!value) return; 15 | if (!hasPermission(value)) { 16 | el.parentNode?.removeChild(el); 17 | } 18 | } 19 | 20 | const mounted = (el: Element, binding: DirectiveBinding) => { 21 | isAuth(el, binding); 22 | }; 23 | 24 | const authDirective: Directive = { 25 | mounted, 26 | }; 27 | 28 | export function setupPermissionDirective(app: App) { 29 | app.directive('auth', authDirective); 30 | } 31 | 32 | export default authDirective; 33 | -------------------------------------------------------------------------------- /src/router/routes/modules/about.ts: -------------------------------------------------------------------------------- 1 | import type { AppRouteModule } from '/@/router/types'; 2 | 3 | import { LAYOUT } from '/@/router/constant'; 4 | import { t } from '/@/hooks/web/useI18n'; 5 | 6 | const dashboard: AppRouteModule = { 7 | path: '/about', 8 | name: 'About', 9 | component: LAYOUT, 10 | redirect: '/about/index', 11 | meta: { 12 | hideChildrenInMenu: true, 13 | icon: 'simple-icons:about-dot-me', 14 | title: t('routes.dashboard.about'), 15 | orderNo: 100000, 16 | hideMenu: true, 17 | }, 18 | children: [ 19 | { 20 | path: 'index', 21 | name: 'AboutPage', 22 | component: () => import('/@/views/sys/about/index.vue'), 23 | meta: { 24 | title: t('routes.dashboard.about'), 25 | icon: 'simple-icons:about-dot-me', 26 | hideMenu: true, 27 | }, 28 | }, 29 | ], 30 | }; 31 | 32 | export default dashboard; 33 | -------------------------------------------------------------------------------- /dist/assets/ErrorAction.97ea071c.js: -------------------------------------------------------------------------------- 1 | import{A as l,a8 as m,b_ as d,a as _,j as f,a0 as e,B as g,a1 as E,a5 as a,w as c}from"./vendor.a45ea33e.js";/* empty css */import{_ as C,I as L,aK as h,b as B,P as I}from"./index.c19540a9.js";const T=l({name:"ErrorAction",components:{Icon:L,Tooltip:m,Badge:d},setup(){const{t:o}=B(),{push:n}=_(),t=h(),r=f(()=>t.getErrorLogListCount);function s(){n(I.ERROR_LOG_PAGE).then(()=>{t.setErrorLogListCount(0)})}return{t:o,getCount:r,handleToErrorList:s}}});function b(o,n,t,r,s,j){const i=e("Icon"),u=e("Badge"),p=e("Tooltip");return g(),E(p,{title:o.t("layout.header.tooltipErrorLog"),placement:"bottom",mouseEnterDelay:.5,onClick:o.handleToErrorList},{default:a(()=>[c(u,{count:o.getCount,offset:[0,10],overflowCount:99},{default:a(()=>[c(i,{icon:"ion:bug-outline"})]),_:1},8,["count"])]),_:1},8,["title","mouseEnterDelay","onClick"])}var R=C(T,[["render",b]]);export{R as default}; 2 | -------------------------------------------------------------------------------- /src/views/sys/error-log/DetailModal.vue: -------------------------------------------------------------------------------- 1 | 6 | 28 | -------------------------------------------------------------------------------- /src/components/CodeEditor/src/codemirror/codeMirror.ts: -------------------------------------------------------------------------------- 1 | import CodeMirror from 'codemirror'; 2 | import './codemirror.css'; 3 | import 'codemirror/theme/idea.css'; 4 | import 'codemirror/theme/material-palenight.css'; 5 | // import 'codemirror/addon/lint/lint.css'; 6 | 7 | // modes 8 | import 'codemirror/mode/javascript/javascript'; 9 | import 'codemirror/mode/css/css'; 10 | import 'codemirror/mode/htmlmixed/htmlmixed'; 11 | // addons 12 | // import 'codemirror/addon/edit/closebrackets'; 13 | // import 'codemirror/addon/edit/closetag'; 14 | // import 'codemirror/addon/comment/comment'; 15 | // import 'codemirror/addon/fold/foldcode'; 16 | // import 'codemirror/addon/fold/foldgutter'; 17 | // import 'codemirror/addon/fold/brace-fold'; 18 | // import 'codemirror/addon/fold/indent-fold'; 19 | // import 'codemirror/addon/lint/json-lint'; 20 | // import 'codemirror/addon/fold/comment-fold'; 21 | export { CodeMirror }; 22 | -------------------------------------------------------------------------------- /src/components/Upload/src/helper.ts: -------------------------------------------------------------------------------- 1 | export function checkFileType(file: File, accepts: string[]) { 2 | const newTypes = accepts.join('|'); 3 | // const reg = /\.(jpg|jpeg|png|gif|txt|doc|docx|xls|xlsx|xml)$/i; 4 | const reg = new RegExp('\\.(' + newTypes + ')$', 'i'); 5 | 6 | return reg.test(file.name); 7 | } 8 | 9 | export function checkImgType(file: File) { 10 | return isImgTypeByName(file.name); 11 | } 12 | 13 | export function isImgTypeByName(name: string) { 14 | return /\.(jpg|jpeg|png|gif)$/i.test(name); 15 | } 16 | 17 | export function getBase64WithFile(file: File) { 18 | return new Promise<{ 19 | result: string; 20 | file: File; 21 | }>((resolve, reject) => { 22 | const reader = new FileReader(); 23 | reader.readAsDataURL(file); 24 | reader.onload = () => resolve({ result: reader.result as string, file }); 25 | reader.onerror = (error) => reject(error); 26 | }); 27 | } 28 | -------------------------------------------------------------------------------- /src/design/index.less: -------------------------------------------------------------------------------- 1 | @import 'transition/index.less'; 2 | @import 'var/index.less'; 3 | @import 'public.less'; 4 | @import 'ant/index.less'; 5 | @import './theme.less'; 6 | 7 | input:-webkit-autofill { 8 | box-shadow: 0 0 0 1000px white inset !important; 9 | } 10 | 11 | :-webkit-autofill { 12 | transition: background-color 5000s ease-in-out 0s !important; 13 | } 14 | 15 | html { 16 | overflow: hidden; 17 | text-size-adjust: 100%; 18 | } 19 | 20 | html, 21 | body { 22 | width: 100%; 23 | height: 100%; 24 | overflow: visible !important; 25 | overflow-x: hidden !important; 26 | 27 | &.color-weak { 28 | filter: invert(80%); 29 | } 30 | 31 | &.gray-mode { 32 | filter: grayscale(100%); 33 | filter: progid:dximagetransform.microsoft.basicimage(grayscale=1); 34 | } 35 | } 36 | 37 | a:focus, 38 | a:active, 39 | button, 40 | div, 41 | svg, 42 | span { 43 | outline: none !important; 44 | } 45 | -------------------------------------------------------------------------------- /src/design/var/easing.less: -------------------------------------------------------------------------------- 1 | // ================================= 2 | // ==============动画函数-=========== 3 | // ================================= 4 | 5 | @ease-base-out: cubic-bezier(0.7, 0.3, 0.1, 1); 6 | @ease-base-in: cubic-bezier(0.9, 0, 0.3, 0.7); 7 | @ease-out: cubic-bezier(0.215, 0.61, 0.355, 1); 8 | @ease-in: cubic-bezier(0.55, 0.055, 0.675, 0.19); 9 | @ease-in-out: cubic-bezier(0.645, 0.045, 0.355, 1); 10 | @ease-out-back: cubic-bezier(0.12, 0.4, 0.29, 1.46); 11 | @ease-in-back: cubic-bezier(0.71, -0.46, 0.88, 0.6); 12 | @ease-in-out-back: cubic-bezier(0.71, -0.46, 0.29, 1.46); 13 | @ease-out-circ: cubic-bezier(0.08, 0.82, 0.17, 1); 14 | @ease-in-circ: cubic-bezier(0.6, 0.04, 0.98, 0.34); 15 | @ease-in-out-circ: cubic-bezier(0.78, 0.14, 0.15, 0.86); 16 | @ease-out-quint: cubic-bezier(0.23, 1, 0.32, 1); 17 | @ease-in-quint: cubic-bezier(0.755, 0.05, 0.855, 0.06); 18 | @ease-in-out-quint: cubic-bezier(0.86, 0, 0.07, 1); 19 | -------------------------------------------------------------------------------- /src/views/dashboard/analysis/data.ts: -------------------------------------------------------------------------------- 1 | export interface GrowCardItem { 2 | icon: string; 3 | title: string; 4 | value: number; 5 | total: number; 6 | color: string; 7 | action: string; 8 | } 9 | 10 | export const growCardList: GrowCardItem[] = [ 11 | { 12 | title: '访问数', 13 | icon: 'visit-count|svg', 14 | value: 2000, 15 | total: 120000, 16 | color: 'green', 17 | action: '月', 18 | }, 19 | { 20 | title: '成交额', 21 | icon: 'total-sales|svg', 22 | value: 20000, 23 | total: 500000, 24 | color: 'blue', 25 | action: '月', 26 | }, 27 | { 28 | title: '下载数', 29 | icon: 'download-count|svg', 30 | value: 8000, 31 | total: 120000, 32 | color: 'orange', 33 | action: '周', 34 | }, 35 | { 36 | title: '成交数', 37 | icon: 'transaction|svg', 38 | value: 5000, 39 | total: 50000, 40 | color: 'purple', 41 | action: '年', 42 | }, 43 | ]; 44 | -------------------------------------------------------------------------------- /src/utils/propTypes.ts: -------------------------------------------------------------------------------- 1 | import { CSSProperties, VNodeChild } from 'vue'; 2 | import { createTypes, VueTypeValidableDef, VueTypesInterface } from 'vue-types'; 3 | 4 | export type VueNode = VNodeChild | JSX.Element; 5 | 6 | type PropTypes = VueTypesInterface & { 7 | readonly style: VueTypeValidableDef; 8 | readonly VNodeChild: VueTypeValidableDef; 9 | // readonly trueBool: VueTypeValidableDef; 10 | }; 11 | 12 | const propTypes = createTypes({ 13 | func: undefined, 14 | bool: undefined, 15 | string: undefined, 16 | number: undefined, 17 | object: undefined, 18 | integer: undefined, 19 | }) as PropTypes; 20 | 21 | propTypes.extend([ 22 | { 23 | name: 'style', 24 | getter: true, 25 | type: [String, Object], 26 | default: undefined, 27 | }, 28 | { 29 | name: 'VNodeChild', 30 | getter: true, 31 | type: undefined, 32 | }, 33 | ]); 34 | export { propTypes }; 35 | -------------------------------------------------------------------------------- /dist/assets/lock.0c0efc31.js: -------------------------------------------------------------------------------- 1 | var l=(e,n,t)=>new Promise((r,s)=>{var c=o=>{try{i(t.next(o))}catch(f){s(f)}},a=o=>{try{i(t.throw(o))}catch(f){s(f)}},i=o=>o.done?r(o.value):Promise.resolve(o.value).then(c,a);i((t=t.apply(e,n)).next())});import{f as I}from"./vendor.a45ea33e.js";import{ag as u,ah as k,f as L}from"./index.c19540a9.js";const d=I({id:"app-lock",state:()=>({lockInfo:u.getLocal(k)}),getters:{getLockInfo(){return this.lockInfo}},actions:{setLockInfo(e){this.lockInfo=Object.assign({},this.lockInfo,e),u.setLocal(k,this.lockInfo,!0)},resetLockInfo(){u.removeLocal(k,!0),this.lockInfo=null},unLock(e){return l(this,null,function*(){var r;const n=L();return((r=this.lockInfo)==null?void 0:r.pwd)===e?(this.resetLockInfo(),!0):yield(()=>l(this,null,function*(){var s;try{const c=(s=n.getUserInfo)==null?void 0:s.username,a=yield n.login({username:c,password:e,goHome:!1,mode:"none"});return a&&this.resetLockInfo(),a}catch(c){return!1}}))()})}}});export{d as u}; 2 | -------------------------------------------------------------------------------- /src/hooks/setting/useMultipleTabSetting.ts: -------------------------------------------------------------------------------- 1 | import type { MultiTabsSetting } from '/#/config'; 2 | 3 | import { computed } from 'vue'; 4 | 5 | import { useAppStore } from '/@/store/modules/app'; 6 | 7 | export function useMultipleTabSetting() { 8 | const appStore = useAppStore(); 9 | 10 | const getShowMultipleTab = computed(() => appStore.getMultiTabsSetting.show); 11 | 12 | const getShowQuick = computed(() => appStore.getMultiTabsSetting.showQuick); 13 | 14 | const getShowRedo = computed(() => appStore.getMultiTabsSetting.showRedo); 15 | 16 | const getShowFold = computed(() => appStore.getMultiTabsSetting.showFold); 17 | 18 | function setMultipleTabSetting(multiTabsSetting: Partial) { 19 | appStore.setProjectConfig({ multiTabsSetting }); 20 | } 21 | return { 22 | setMultipleTabSetting, 23 | getShowMultipleTab, 24 | getShowQuick, 25 | getShowRedo, 26 | getShowFold, 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/Modal/src/components/Modal.tsx: -------------------------------------------------------------------------------- 1 | import { Modal } from 'ant-design-vue'; 2 | import { defineComponent, toRefs, unref } from 'vue'; 3 | import { basicProps } from '../props'; 4 | import { useModalDragMove } from '../hooks/useModalDrag'; 5 | import { useAttrs } from '/@/hooks/core/useAttrs'; 6 | import { extendSlots } from '/@/utils/helper/tsxHelper'; 7 | 8 | export default defineComponent({ 9 | name: 'Modal', 10 | inheritAttrs: false, 11 | props: basicProps, 12 | emits: ['cancel'], 13 | setup(props, { slots }) { 14 | const { visible, draggable, destroyOnClose } = toRefs(props); 15 | const attrs = useAttrs(); 16 | useModalDragMove({ 17 | visible, 18 | destroyOnClose, 19 | draggable, 20 | }); 21 | 22 | return () => { 23 | const propsData = { ...unref(attrs), ...props } as Recordable; 24 | return {extendSlots(slots)}; 25 | }; 26 | }, 27 | }); 28 | -------------------------------------------------------------------------------- /src/design/var/breakpoint.less: -------------------------------------------------------------------------------- 1 | // ================================= 2 | // ==============屏幕断点============ 3 | // ================================= 4 | 5 | // Extra small screen / phone 6 | @screen-xs: 480px; 7 | @screen-xs-min: @screen-xs; 8 | 9 | // Small screen / tablet 10 | @screen-sm: 576px; 11 | @screen-sm-min: @screen-sm; 12 | 13 | // Medium screen / desktop 14 | @screen-md: 768px; 15 | @screen-md-min: @screen-md; 16 | 17 | // Large screen / wide desktop 18 | @screen-lg: 992px; 19 | @screen-lg-min: @screen-lg; 20 | 21 | // Extra large screen / full hd 22 | @screen-xl: 1200px; 23 | @screen-xl-min: @screen-xl; 24 | 25 | // Extra extra large screen / large desktop 26 | @screen-2xl: 1600px; 27 | @screen-2xl-min: @screen-2xl; 28 | 29 | @screen-xs-max: (@screen-sm-min - 1px); 30 | @screen-sm-max: (@screen-md-min - 1px); 31 | @screen-md-max: (@screen-lg-min - 1px); 32 | @screen-lg-max: (@screen-xl-min - 1px); 33 | @screen-xl-max: (@screen-2xl-min - 1px); 34 | -------------------------------------------------------------------------------- /src/views/dashboard/analysis/components/SiteAnalysis.vue: -------------------------------------------------------------------------------- 1 | 16 | 39 | -------------------------------------------------------------------------------- /src/components/Table/src/components/settings/RedoSetting.vue: -------------------------------------------------------------------------------- 1 | 9 | 34 | -------------------------------------------------------------------------------- /src/hooks/event/useWindowSizeFn.ts: -------------------------------------------------------------------------------- 1 | import { tryOnMounted, tryOnUnmounted } from '@vueuse/core'; 2 | import { useDebounceFn } from '@vueuse/core'; 3 | 4 | interface WindowSizeOptions { 5 | once?: boolean; 6 | immediate?: boolean; 7 | listenerOptions?: AddEventListenerOptions | boolean; 8 | } 9 | 10 | export function useWindowSizeFn(fn: Fn, wait = 150, options?: WindowSizeOptions) { 11 | let handler = () => { 12 | fn(); 13 | }; 14 | const handleSize = useDebounceFn(handler, wait); 15 | handler = handleSize; 16 | 17 | const start = () => { 18 | if (options && options.immediate) { 19 | handler(); 20 | } 21 | window.addEventListener('resize', handler); 22 | }; 23 | 24 | const stop = () => { 25 | window.removeEventListener('resize', handler); 26 | }; 27 | 28 | tryOnMounted(() => { 29 | start(); 30 | }); 31 | 32 | tryOnUnmounted(() => { 33 | stop(); 34 | }); 35 | return [start, stop]; 36 | } 37 | -------------------------------------------------------------------------------- /dist/assets/index.53ebad0e.js: -------------------------------------------------------------------------------- 1 | var p=Object.defineProperty,c=Object.defineProperties;var x=Object.getOwnPropertyDescriptors;var i=Object.getOwnPropertySymbols;var j=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable;var r=(o,e,s)=>e in o?p(o,e,{enumerable:!0,configurable:!0,writable:!0,value:s}):o[e]=s,a=(o,e)=>{for(var s in e||(e={}))j.call(e,s)&&r(o,s,e[s]);if(i)for(var s of i(e))_.call(e,s)&&r(o,s,e[s]);return o},d=(o,e)=>c(o,x(e));import{M as u,a as f,L as l}from"./index.809887c2.js";import{A as m,B as g,D as k,w as n,u as t}from"./vendor.a45ea33e.js";import"./index.c19540a9.js";/* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css *//* empty css */const B=m({name:"Log"});function L(o){return(e,s)=>(g(),k("div",null,[n(t(u)),n(t(f)),n(t(l))]))}const V=m(d(a({},B),{setup:L}));export{V as default}; 2 | -------------------------------------------------------------------------------- /src/utils/auth/index.ts: -------------------------------------------------------------------------------- 1 | import { Persistent, BasicKeys } from '/@/utils/cache/persistent'; 2 | import { CacheTypeEnum } from '/@/enums/cacheEnum'; 3 | import projectSetting from '/@/settings/projectSetting'; 4 | import { TOKEN_KEY } from '/@/enums/cacheEnum'; 5 | 6 | const { permissionCacheType } = projectSetting; 7 | const isLocal = permissionCacheType === CacheTypeEnum.LOCAL; 8 | 9 | export function getToken() { 10 | return getAuthCache(TOKEN_KEY); 11 | } 12 | 13 | export function getAuthCache(key: BasicKeys) { 14 | const fn = isLocal ? Persistent.getLocal : Persistent.getSession; 15 | return fn(key) as T; 16 | } 17 | 18 | export function setAuthCache(key: BasicKeys, value) { 19 | const fn = isLocal ? Persistent.setLocal : Persistent.setSession; 20 | return fn(key, value, true); 21 | } 22 | 23 | export function clearAuthCache(immediate = true) { 24 | const fn = isLocal ? Persistent.clearLocal : Persistent.clearSession; 25 | return fn(immediate); 26 | } 27 | -------------------------------------------------------------------------------- /src/hooks/web/useFullContent.ts: -------------------------------------------------------------------------------- 1 | import { computed, unref } from 'vue'; 2 | 3 | import { useAppStore } from '/@/store/modules/app'; 4 | 5 | import { useRouter } from 'vue-router'; 6 | 7 | /** 8 | * @description: Full screen display content 9 | */ 10 | export const useFullContent = () => { 11 | const appStore = useAppStore(); 12 | const router = useRouter(); 13 | const { currentRoute } = router; 14 | 15 | // Whether to display the content in full screen without displaying the menu 16 | const getFullContent = computed(() => { 17 | // Query parameters, the full screen is displayed when the address bar has a full parameter 18 | const route = unref(currentRoute); 19 | const query = route.query; 20 | if (query && Reflect.has(query, '__full__')) { 21 | return true; 22 | } 23 | // Return to the configuration in the configuration file 24 | return appStore.getProjectConfig.fullContent; 25 | }); 26 | 27 | return { getFullContent }; 28 | }; 29 | -------------------------------------------------------------------------------- /src/utils/helper/tsxHelper.tsx: -------------------------------------------------------------------------------- 1 | import { Slots } from 'vue'; 2 | import { isFunction } from '/@/utils/is'; 3 | 4 | /** 5 | * @description: Get slot to prevent empty error 6 | */ 7 | export function getSlot(slots: Slots, slot = 'default', data?: any) { 8 | if (!slots || !Reflect.has(slots, slot)) { 9 | return null; 10 | } 11 | if (!isFunction(slots[slot])) { 12 | console.error(`${slot} is not a function!`); 13 | return null; 14 | } 15 | const slotFn = slots[slot]; 16 | if (!slotFn) return null; 17 | return slotFn(data); 18 | } 19 | 20 | /** 21 | * extends slots 22 | * @param slots 23 | * @param excludeKeys 24 | */ 25 | export function extendSlots(slots: Slots, excludeKeys: string[] = []) { 26 | const slotKeys = Object.keys(slots); 27 | const ret: any = {}; 28 | slotKeys.map((key) => { 29 | if (excludeKeys.includes(key)) { 30 | return null; 31 | } 32 | ret[key] = () => getSlot(slots, key); 33 | }); 34 | return ret; 35 | } 36 | -------------------------------------------------------------------------------- /src/views/sys/login/LoginFormTitle.vue: -------------------------------------------------------------------------------- 1 | 6 | 26 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | NODE_ENV=production 2 | # Whether to open mock 3 | VITE_USE_MOCK = false 4 | 5 | # public path 6 | VITE_PUBLIC_PATH = / 7 | 8 | # Delete console 9 | VITE_DROP_CONSOLE = true 10 | 11 | # Whether to enable gzip or brotli compression 12 | # Optional: gzip | brotli | none 13 | # If you need multiple forms, you can use `,` to separate 14 | VITE_BUILD_COMPRESS = 'none' 15 | 16 | # Whether to delete origin files when using compress, default false 17 | VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false 18 | 19 | # Basic interface address SPA 20 | VITE_GLOB_API_URL=http://localhost:3000/api 21 | 22 | # File upload address, optional 23 | # It can be forwarded by nginx or write the actual address directly 24 | VITE_GLOB_UPLOAD_URL=/upload 25 | 26 | # Interface prefix 27 | VITE_GLOB_API_URL_PREFIX= 28 | 29 | # Whether to enable image compression 30 | VITE_USE_IMAGEMIN= true 31 | 32 | # use pwa 33 | VITE_USE_PWA = false 34 | 35 | # Is it compatible with older browsers 36 | VITE_LEGACY = false 37 | -------------------------------------------------------------------------------- /dist/assets/DynamicInfo.49c499a3.js: -------------------------------------------------------------------------------- 1 | import{A as l,bN as o,a0 as m,B as p,a1 as _,a5 as e,w as a,u as t,ad as n,J as r,H as f,a4 as x,bL as I}from"./vendor.a45ea33e.js";/* empty css *//* empty css *//* empty css *//* empty css */import{d as j}from"./data.8ddc77c3.js";import{I as L}from"./index.c19540a9.js";const h=n("\u66F4\u591A"),B=["innerHTML"],H=l({setup(b){const i=o.Item,d=o.Item.Meta;return(c,v)=>{const u=m("a-button");return p(),_(t(I),x({title:"\u6700\u65B0\u52A8\u6001"},c.$attrs),{extra:e(()=>[a(u,{type:"link",size:"small"},{default:e(()=>[h]),_:1})]),default:e(()=>[a(t(o),{"item-layout":"horizontal","data-source":t(j)},{renderItem:e(({item:s})=>[a(t(i),null,{default:e(()=>[a(t(d),null,{description:e(()=>[n(r(s.date),1)]),title:e(()=>[n(r(s.name)+" ",1),f("span",{innerHTML:s.desc},null,8,B)]),avatar:e(()=>[a(t(L),{icon:s.avatar,size:30},null,8,["icon"])]),_:2},1024)]),_:2},1024)]),_:1},8,["data-source"])]),_:1},16)}}});export{H as default}; 2 | -------------------------------------------------------------------------------- /src/layouts/iframe/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 30 | -------------------------------------------------------------------------------- /src/layouts/page/transition.ts: -------------------------------------------------------------------------------- 1 | import type { FunctionalComponent } from 'vue'; 2 | import type { RouteLocation } from 'vue-router'; 3 | 4 | export interface DefaultContext { 5 | Component: FunctionalComponent & { type: Recordable }; 6 | route: RouteLocation; 7 | } 8 | 9 | export function getTransitionName({ 10 | route, 11 | openCache, 12 | cacheTabs, 13 | enableTransition, 14 | def, 15 | }: Pick & { 16 | enableTransition: boolean; 17 | openCache: boolean; 18 | def: string; 19 | cacheTabs: string[]; 20 | }): string | undefined { 21 | if (!enableTransition) { 22 | return undefined; 23 | } 24 | 25 | const isInCache = cacheTabs.includes(route.name as string); 26 | const transitionName = 'fade-slide'; 27 | let name: string | undefined = transitionName; 28 | 29 | if (openCache) { 30 | name = isInCache && route.meta.loaded ? transitionName : undefined; 31 | } 32 | return name || (route.meta.transitionName as string) || def; 33 | } 34 | -------------------------------------------------------------------------------- /src/assets/svg/preview/resume.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/settings/designSetting.ts: -------------------------------------------------------------------------------- 1 | import { ThemeEnum } from '../enums/appEnum'; 2 | 3 | export const prefixCls = 'vben'; 4 | 5 | export const darkMode = ThemeEnum.LIGHT; 6 | 7 | // app theme preset color 8 | export const APP_PRESET_COLOR_LIST: string[] = [ 9 | '#0960bd', 10 | '#0084f4', 11 | '#009688', 12 | '#536dfe', 13 | '#ff5c93', 14 | '#ee4f12', 15 | '#0096c7', 16 | '#9c27b0', 17 | '#ff9800', 18 | ]; 19 | 20 | // header preset color 21 | export const HEADER_PRESET_BG_COLOR_LIST: string[] = [ 22 | '#ffffff', 23 | '#151515', 24 | '#009688', 25 | '#5172DC', 26 | '#018ffb', 27 | '#409eff', 28 | '#e74c3c', 29 | '#24292e', 30 | '#394664', 31 | '#001529', 32 | '#383f45', 33 | ]; 34 | 35 | // sider preset color 36 | export const SIDE_BAR_BG_COLOR_LIST: string[] = [ 37 | '#001529', 38 | '#212121', 39 | '#273352', 40 | '#ffffff', 41 | '#191b24', 42 | '#191a23', 43 | '#304156', 44 | '#001628', 45 | '#28333E', 46 | '#344058', 47 | '#383f45', 48 | ]; 49 | -------------------------------------------------------------------------------- /src/utils/cq/message.ts: -------------------------------------------------------------------------------- 1 | import { MessageElem } from 'oicq/lib/message/elements'; 2 | import qs from 'qs'; 3 | export function genCqcode(content: MessageElem[]) { 4 | let cqcode = ''; 5 | for (const elem of content) { 6 | if (elem.type === 'text') { 7 | cqcode += elem.text; 8 | continue; 9 | } 10 | const tmp = { ...elem } as Partial; 11 | delete tmp.type; 12 | const str = qs.stringify(tmp as NodeJS.Dict, { 13 | delimiter: ',', 14 | indices: false, 15 | encoder: (s) => { 16 | const t = s.toString(); 17 | t.replace(/&|,|\[|\]/g, escapeCQInside); 18 | return t; 19 | }, 20 | }); 21 | cqcode += '[CQ:' + elem.type + (str ? ',' : '') + str + ']'; 22 | } 23 | return cqcode; 24 | } 25 | 26 | function escapeCQInside(s: string) { 27 | if (s === '&') return '&'; 28 | if (s === ',') return ','; 29 | if (s === '[') return '['; 30 | if (s === ']') return ']'; 31 | return ''; 32 | } 33 | -------------------------------------------------------------------------------- /src/views/dashboard/analysis/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 26 | -------------------------------------------------------------------------------- /dist/assets/TypePicker.9a3d6054.js: -------------------------------------------------------------------------------- 1 | import{A as r,a8 as i,a0 as l,B as s,D as a,aa as c,ao as d,K as o,a1 as _,a5 as u,H as n,L as f,N as m}from"./vendor.a45ea33e.js";import{_ as y,a as v}from"./index.c19540a9.js";const C=r({name:"MenuTypePicker",components:{Tooltip:i},props:{menuTypeList:{type:Array,defualt:()=>[]},handler:{type:Function,default:()=>({})},def:{type:String,default:""}},setup(){const{prefixCls:e}=v("setting-menu-type-picker");return{prefixCls:e}}}),k=e=>(f("data-v-7796e69a"),e=e(),m(),e),h=["onClick"],$=k(()=>n("div",{class:"mix-sidebar"},null,-1)),T=[$];function g(e,x,B,I,L,S){const p=l("Tooltip");return s(),a("div",{class:o(e.prefixCls)},[(s(!0),a(c,null,d(e.menuTypeList||[],t=>(s(),_(p,{key:t.title,title:t.title,placement:"bottom"},{default:u(()=>[n("div",{onClick:P=>e.handler(t),class:o([`${e.prefixCls}__item`,`${e.prefixCls}__item--${t.type}`,{[`${e.prefixCls}__item--active`]:e.def===t.type}])},T,10,h)]),_:2},1032,["title"]))),128))],2)}var w=y(C,[["render",g],["__scopeId","data-v-7796e69a"]]);export{w as default}; 2 | -------------------------------------------------------------------------------- /src/components/Tinymce/src/tinymce.ts: -------------------------------------------------------------------------------- 1 | // Any plugins you want to setting has to be imported 2 | // Detail plugins list see https://www.tinymce.com/docs/plugins/ 3 | // Custom builds see https://www.tinymce.com/download/custom-builds/ 4 | // colorpicker/contextmenu/textcolor plugin is now built in to the core editor, please remove it from your editor configuration 5 | 6 | export const plugins = [ 7 | 'advlist anchor autolink autosave code codesample directionality fullscreen hr insertdatetime link lists media nonbreaking noneditable pagebreak paste preview print save searchreplace spellchecker tabfocus template textpattern visualblocks visualchars wordcount', 8 | ]; 9 | 10 | export const toolbar = [ 11 | 'fontsizeselect lineheight searchreplace bold italic underline strikethrough alignleft aligncenter alignright outdent indent blockquote undo redo removeformat subscript superscript code codesample', 12 | 'hr bullist numlist link preview anchor pagebreak insertdatetime media forecolor backcolor fullscreen', 13 | ]; 14 | -------------------------------------------------------------------------------- /src/enums/menuEnum.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description: menu type 3 | */ 4 | export enum MenuTypeEnum { 5 | // left menu 6 | SIDEBAR = 'sidebar', 7 | 8 | MIX_SIDEBAR = 'mix-sidebar', 9 | // mixin menu 10 | MIX = 'mix', 11 | // top menu 12 | TOP_MENU = 'top-menu', 13 | } 14 | 15 | // 折叠触发器位置 16 | export enum TriggerEnum { 17 | // 不显示 18 | NONE = 'NONE', 19 | // 菜单底部 20 | FOOTER = 'FOOTER', 21 | // 头部 22 | HEADER = 'HEADER', 23 | } 24 | 25 | export type Mode = 'vertical' | 'vertical-right' | 'horizontal' | 'inline'; 26 | 27 | // menu mode 28 | export enum MenuModeEnum { 29 | VERTICAL = 'vertical', 30 | HORIZONTAL = 'horizontal', 31 | VERTICAL_RIGHT = 'vertical-right', 32 | INLINE = 'inline', 33 | } 34 | 35 | export enum MenuSplitTyeEnum { 36 | NONE, 37 | TOP, 38 | LEFT, 39 | } 40 | 41 | export enum TopMenuAlignEnum { 42 | CENTER = 'center', 43 | START = 'start', 44 | END = 'end', 45 | } 46 | 47 | export enum MixSidebarTriggerEnum { 48 | HOVER = 'hover', 49 | CLICK = 'click', 50 | } 51 | -------------------------------------------------------------------------------- /src/hooks/setting/index.ts: -------------------------------------------------------------------------------- 1 | import type { GlobConfig } from '/#/config'; 2 | 3 | import { warn } from '/@/utils/log'; 4 | import { getAppEnvConfig } from '/@/utils/env'; 5 | 6 | export const useGlobSetting = (): Readonly => { 7 | const { 8 | VITE_GLOB_APP_TITLE, 9 | VITE_GLOB_API_URL, 10 | VITE_GLOB_APP_SHORT_NAME, 11 | VITE_GLOB_API_URL_PREFIX, 12 | VITE_GLOB_UPLOAD_URL, 13 | } = getAppEnvConfig(); 14 | 15 | if (!/[a-zA-Z\_]*/.test(VITE_GLOB_APP_SHORT_NAME)) { 16 | warn( 17 | `VITE_GLOB_APP_SHORT_NAME Variables can only be characters/underscores, please modify in the environment variables and re-running.`, 18 | ); 19 | } 20 | 21 | // Take global configuration 22 | const glob: Readonly = { 23 | title: VITE_GLOB_APP_TITLE, 24 | apiUrl: VITE_GLOB_API_URL, 25 | shortName: VITE_GLOB_APP_SHORT_NAME, 26 | urlPrefix: VITE_GLOB_API_URL_PREFIX, 27 | uploadUrl: VITE_GLOB_UPLOAD_URL, 28 | }; 29 | return glob as Readonly; 30 | }; 31 | -------------------------------------------------------------------------------- /src/layouts/default/header/components/user-dropdown/DropMenuItem.vue: -------------------------------------------------------------------------------- 1 | 9 | 32 | -------------------------------------------------------------------------------- /dist/assets/index.32fb0083.css: -------------------------------------------------------------------------------- 1 | .ant-back-top{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-size:14px;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:tnum;position:fixed;right:100px;bottom:50px;z-index:10;width:40px;height:40px;cursor:pointer}.ant-back-top:empty{display:none}.ant-back-top-rtl{right:auto;left:100px;direction:rtl}.ant-back-top-content{width:40px;height:40px;overflow:hidden;color:#fff;text-align:center;background-color:#00000073;border-radius:20px;transition:all .3s}.ant-back-top-content:hover{background-color:#000000d9;transition:all .3s}.ant-back-top-icon{font-size:24px;line-height:40px}@media screen and (max-width: 768px){.ant-back-top{right:60px}}@media screen and (max-width: 480px){.ant-back-top{right:20px}}.vben-setting-drawer-fearure{position:absolute;top:45%;right:0;z-index:10;display:flex;padding:10px;color:#fff;cursor:pointer;background-color:#0960bd;border-radius:6px 0 0 6px;justify-content:center;align-items:center}.vben-setting-drawer-fearure svg{width:1em;height:1em} 2 | -------------------------------------------------------------------------------- /src/directives/repeatClick.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Prevent repeated clicks 3 | * @Example v-repeat-click="()=>{}" 4 | */ 5 | import { on, once } from '/@/utils/domUtils'; 6 | import type { Directive, DirectiveBinding } from 'vue'; 7 | 8 | const repeatDirective: Directive = { 9 | beforeMount(el: Element, binding: DirectiveBinding) { 10 | let interval: Nullable = null; 11 | let startTime = 0; 12 | const handler = (): void => binding?.value(); 13 | const clear = (): void => { 14 | if (Date.now() - startTime < 100) { 15 | handler(); 16 | } 17 | interval && clearInterval(interval); 18 | interval = null; 19 | }; 20 | 21 | on(el, 'mousedown', (e: MouseEvent): void => { 22 | if ((e as any).button !== 0) return; 23 | startTime = Date.now(); 24 | once(document as any, 'mouseup', clear); 25 | interval && clearInterval(interval); 26 | interval = setInterval(handler, 100); 27 | }); 28 | }, 29 | }; 30 | 31 | export default repeatDirective; 32 | -------------------------------------------------------------------------------- /src/logics/mitt/routeChange.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Used to monitor routing changes to change the status of menus and tabs. There is no need to monitor the route, because the route status change is affected by the page rendering time, which will be slow 3 | */ 4 | 5 | import mitt from '/@/utils/mitt'; 6 | import type { RouteLocationNormalized } from 'vue-router'; 7 | import { getRawRoute } from '/@/utils'; 8 | 9 | const emitter = mitt(); 10 | 11 | const key = Symbol(); 12 | 13 | let lastChangeTab: RouteLocationNormalized; 14 | 15 | export function setRouteChange(lastChangeRoute: RouteLocationNormalized) { 16 | const r = getRawRoute(lastChangeRoute); 17 | emitter.emit(key, r); 18 | lastChangeTab = r; 19 | } 20 | 21 | export function listenerRouteChange( 22 | callback: (route: RouteLocationNormalized) => void, 23 | immediate = true, 24 | ) { 25 | emitter.on(key, callback); 26 | immediate && lastChangeTab && callback(lastChangeTab); 27 | } 28 | 29 | export function removeTabChangeListener() { 30 | emitter.clear(); 31 | } 32 | -------------------------------------------------------------------------------- /src/assets/svg/preview/unscale.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/components/Description/src/useDescription.ts: -------------------------------------------------------------------------------- 1 | import type { DescriptionProps, DescInstance, UseDescReturnType } from './typing'; 2 | import { ref, getCurrentInstance, unref } from 'vue'; 3 | import { isProdMode } from '/@/utils/env'; 4 | 5 | export function useDescription(props?: Partial): UseDescReturnType { 6 | if (!getCurrentInstance()) { 7 | throw new Error('useDescription() can only be used inside setup() or functional components!'); 8 | } 9 | const desc = ref>(null); 10 | const loaded = ref(false); 11 | 12 | function register(instance: DescInstance) { 13 | if (unref(loaded) && isProdMode()) { 14 | return; 15 | } 16 | desc.value = instance; 17 | props && instance.setDescProps(props); 18 | loaded.value = true; 19 | } 20 | 21 | const methods: DescInstance = { 22 | setDescProps: (descProps: Partial): void => { 23 | unref(desc)?.setDescProps(descProps); 24 | }, 25 | }; 26 | 27 | return [register, methods]; 28 | } 29 | -------------------------------------------------------------------------------- /src/layouts/default/trigger/HeaderTrigger.vue: -------------------------------------------------------------------------------- 1 | 6 | 26 | -------------------------------------------------------------------------------- /dist/assets/index.dfe67a3a.js: -------------------------------------------------------------------------------- 1 | import{A as v,r as s,j as x,u as e,B as y,D as w,w as H,a5 as S,H as j,K as u,X as f,aO as z}from"./vendor.a45ea33e.js";import{u as C}from"./useWindowSizeFn.48fcda03.js";import{p as R,a as B,_ as F}from"./index.c19540a9.js";import{a as L}from"./useContentViewHeight.4cbdde4a.js";const V=["src"],W=v({props:{frameSrc:R.string.def("")},setup(p){const i=s(!0),m=s(50),o=s(window.innerHeight),r=s(),{headerHeightRef:g}=L(),{prefixCls:c}=B("iframe-page");C(l,150,{immediate:!0});const d=x(()=>({height:`${e(o)}px`}));function l(){const n=e(r);if(!n)return;const t=g.value;m.value=t,o.value=window.innerHeight-t;const a=document.documentElement.clientHeight-t;n.style.height=`${a}px`}function _(){i.value=!1,l()}return(n,t)=>(y(),w("div",{class:u(e(c)),style:f(e(d))},[H(e(z),{spinning:i.value,size:"large",style:f(e(d))},{default:S(()=>[j("iframe",{src:p.frameSrc,class:u(`${e(c)}__main`),ref:(a,h)=>{h.frameRef=a,r.value=a},onLoad:_},null,42,V)]),_:1},8,["spinning","style"])],6))}});var E=F(W,[["__scopeId","data-v-7cb5d70d"]]);export{E as default}; 2 | -------------------------------------------------------------------------------- /src/components/Table/src/const.ts: -------------------------------------------------------------------------------- 1 | import componentSetting from '/@/settings/componentSetting'; 2 | 3 | const { table } = componentSetting; 4 | 5 | const { 6 | pageSizeOptions, 7 | defaultPageSize, 8 | fetchSetting, 9 | defaultSize, 10 | defaultSortFn, 11 | defaultFilterFn, 12 | } = table; 13 | 14 | export const ROW_KEY = 'key'; 15 | 16 | // Optional display number per page; 17 | export const PAGE_SIZE_OPTIONS = pageSizeOptions; 18 | 19 | // Number of items displayed per page 20 | export const PAGE_SIZE = defaultPageSize; 21 | 22 | // Common interface field settings 23 | export const FETCH_SETTING = fetchSetting; 24 | 25 | // Default Size 26 | export const DEFAULT_SIZE = defaultSize; 27 | 28 | // Configure general sort function 29 | export const DEFAULT_SORT_FN = defaultSortFn; 30 | 31 | export const DEFAULT_FILTER_FN = defaultFilterFn; 32 | 33 | // Default layout of table cells 34 | export const DEFAULT_ALIGN = 'center'; 35 | 36 | export const INDEX_COLUMN_FLAG = 'INDEX'; 37 | 38 | export const ACTION_COLUMN_FLAG = 'ACTION'; 39 | -------------------------------------------------------------------------------- /src/components/Qrcode/src/typing.ts: -------------------------------------------------------------------------------- 1 | import type { QRCodeSegment, QRCodeRenderersOptions } from 'qrcode'; 2 | 3 | export type ContentType = string | QRCodeSegment[]; 4 | 5 | export type { QRCodeRenderersOptions }; 6 | 7 | export type LogoType = { 8 | src: string; 9 | logoSize: number; 10 | borderColor: string; 11 | bgColor: string; 12 | borderSize: number; 13 | crossOrigin: string; 14 | borderRadius: number; 15 | logoRadius: number; 16 | }; 17 | 18 | export interface RenderQrCodeParams { 19 | canvas: any; 20 | content: ContentType; 21 | width?: number; 22 | options?: QRCodeRenderersOptions; 23 | logo?: LogoType | string; 24 | image?: HTMLImageElement; 25 | downloadName?: string; 26 | download?: boolean | Fn; 27 | } 28 | 29 | export type ToCanvasFn = (options: RenderQrCodeParams) => Promise; 30 | 31 | export interface QrCodeActionType { 32 | download: (fileName?: string) => void; 33 | } 34 | 35 | export interface QrcodeDoneEventParams { 36 | url: string; 37 | ctx?: CanvasRenderingContext2D | null; 38 | } 39 | -------------------------------------------------------------------------------- /src/hooks/setting/useTransitionSetting.ts: -------------------------------------------------------------------------------- 1 | import type { TransitionSetting } from '/#/config'; 2 | 3 | import { computed } from 'vue'; 4 | 5 | import { useAppStore } from '/@/store/modules/app'; 6 | 7 | export function useTransitionSetting() { 8 | const appStore = useAppStore(); 9 | 10 | const getEnableTransition = computed(() => appStore.getTransitionSetting?.enable); 11 | 12 | const getOpenNProgress = computed(() => appStore.getTransitionSetting?.openNProgress); 13 | 14 | const getOpenPageLoading = computed((): boolean => { 15 | return !!appStore.getTransitionSetting?.openPageLoading; 16 | }); 17 | 18 | const getBasicTransition = computed(() => appStore.getTransitionSetting?.basicTransition); 19 | 20 | function setTransitionSetting(transitionSetting: Partial) { 21 | appStore.setProjectConfig({ transitionSetting }); 22 | } 23 | return { 24 | setTransitionSetting, 25 | 26 | getEnableTransition, 27 | getOpenNProgress, 28 | getOpenPageLoading, 29 | getBasicTransition, 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/enums/appEnum.ts: -------------------------------------------------------------------------------- 1 | export const SIDE_BAR_MINI_WIDTH = 48; 2 | export const SIDE_BAR_SHOW_TIT_MINI_WIDTH = 80; 3 | 4 | export enum ContentEnum { 5 | // auto width 6 | FULL = 'full', 7 | // fixed width 8 | FIXED = 'fixed', 9 | } 10 | 11 | // menu theme enum 12 | export enum ThemeEnum { 13 | DARK = 'dark', 14 | LIGHT = 'light', 15 | } 16 | 17 | export enum SettingButtonPositionEnum { 18 | AUTO = 'auto', 19 | HEADER = 'header', 20 | FIXED = 'fixed', 21 | } 22 | 23 | export enum SessionTimeoutProcessingEnum { 24 | ROUTE_JUMP, 25 | PAGE_COVERAGE, 26 | } 27 | 28 | /** 29 | * 权限模式 30 | */ 31 | export enum PermissionModeEnum { 32 | // role 33 | ROLE = 'ROLE', 34 | // black 35 | BACK = 'BACK', 36 | // route mapping 37 | ROUTE_MAPPING = 'ROUTE_MAPPING', 38 | } 39 | 40 | // Route switching animation 41 | export enum RouterTransitionEnum { 42 | ZOOM_FADE = 'zoom-fade', 43 | ZOOM_OUT = 'zoom-out', 44 | FADE_SIDE = 'fade-slide', 45 | FADE = 'fade', 46 | FADE_BOTTOM = 'fade-bottom', 47 | FADE_SCALE = 'fade-scale', 48 | } 49 | -------------------------------------------------------------------------------- /src/components/Bot/index.ts: -------------------------------------------------------------------------------- 1 | import { withInstall } from '/@/utils'; 2 | import botSwitch from './src/BotSwitch.vue'; 3 | import messageSendBox from './src/MessageSendBox.vue'; 4 | import messageWebsocket from './src/MessageWebsocket.vue'; 5 | import logWebsocket from './src/LogWebsocket.vue'; 6 | import groupAndFriendSelect from './src/select/GroupAndFriendSelect.vue'; 7 | import groupSelect from './src/select/GroupSelect.vue'; 8 | import groupMemberSelect from './src/select/GroupMemberSelect.vue'; 9 | import friendSelect from './src/select/FriendSelect.vue'; 10 | 11 | export const BotSwitch = withInstall(botSwitch); 12 | export const MessageSendBox = withInstall(messageSendBox); 13 | export const MessageWebsocket = withInstall(messageWebsocket); 14 | export const LogWebsocket = withInstall(logWebsocket); 15 | export const GroupAndFriendSelect = withInstall(groupAndFriendSelect); 16 | export const GroupSelect = withInstall(groupSelect); 17 | export const GroupMemberSelect = withInstall(groupMemberSelect); 18 | export const FriendSelect = withInstall(friendSelect); 19 | -------------------------------------------------------------------------------- /src/router/guard/stateGuard.ts: -------------------------------------------------------------------------------- 1 | import type { Router } from 'vue-router'; 2 | import { useAppStore } from '/@/store/modules/app'; 3 | import { useMultipleTabStore } from '/@/store/modules/multipleTab'; 4 | import { useUserStore } from '/@/store/modules/user'; 5 | import { usePermissionStore } from '/@/store/modules/permission'; 6 | import { PageEnum } from '/@/enums/pageEnum'; 7 | import { removeTabChangeListener } from '/@/logics/mitt/routeChange'; 8 | 9 | export function createStateGuard(router: Router) { 10 | router.afterEach((to) => { 11 | // Just enter the login page and clear the authentication information 12 | if (to.path === PageEnum.BASE_LOGIN) { 13 | const tabStore = useMultipleTabStore(); 14 | const userStore = useUserStore(); 15 | const appStore = useAppStore(); 16 | const permissionStore = usePermissionStore(); 17 | appStore.resetAllState(); 18 | permissionStore.resetState(); 19 | tabStore.resetState(); 20 | userStore.resetState(); 21 | removeTabChangeListener(); 22 | } 23 | }); 24 | } 25 | -------------------------------------------------------------------------------- /src/layouts/default/tabs/components/TabRedo.vue: -------------------------------------------------------------------------------- 1 | 6 | 34 | -------------------------------------------------------------------------------- /src/router/routes/modules/dashboard.ts: -------------------------------------------------------------------------------- 1 | import type { AppRouteModule } from '/@/router/types'; 2 | 3 | import { LAYOUT } from '/@/router/constant'; 4 | import { t } from '/@/hooks/web/useI18n'; 5 | 6 | const dashboard: AppRouteModule = { 7 | path: '/dashboard', 8 | name: 'Dashboard', 9 | component: LAYOUT, 10 | redirect: '/dashboard/analysis', 11 | meta: { 12 | orderNo: 10, 13 | icon: 'ion:grid-outline', 14 | title: t('routes.dashboard.dashboard'), 15 | hideMenu: true, 16 | }, 17 | children: [ 18 | { 19 | path: 'analysis', 20 | name: 'Analysis', 21 | component: () => import('/@/views/dashboard/analysis/index.vue'), 22 | meta: { 23 | // affix: true, 24 | title: t('routes.dashboard.analysis'), 25 | }, 26 | }, 27 | { 28 | path: 'workbench', 29 | name: 'Workbench', 30 | component: () => import('/@/views/dashboard/workbench/index.vue'), 31 | meta: { 32 | title: t('routes.dashboard.workbench'), 33 | }, 34 | }, 35 | ], 36 | }; 37 | 38 | export default dashboard; 39 | -------------------------------------------------------------------------------- /dist/assets/index.f8765bcc.js: -------------------------------------------------------------------------------- 1 | import e from"./GrowCard.133f1dee.js";import i from"./SiteAnalysis.60a5a3b3.js";import o from"./VisitSource.346c45ae.js";import l from"./VisitRadar.e4031646.js";import n from"./SalesProductPie.82502710.js";import{A as r,r as t,B as m,D as d,w as a,H as c}from"./vendor.a45ea33e.js";import"./index.c19540a9.js";/* empty css *//* empty css *//* empty css *//* empty css */import"./VisitAnalysis.ad7c818e.js";import"./useECharts.9a72bc67.js";import"./props.f48aca0b.js";import"./VisitAnalysisBar.5e71d8fc.js";const p={class:"p-4"},f={class:"md:flex enter-y"},N=r({setup(u){const s=t(!0);return setTimeout(()=>{s.value=!1},1500),(_,j)=>(m(),d("div",p,[a(e,{loading:s.value,class:"enter-y"},null,8,["loading"]),a(i,{class:"!my-4 enter-y",loading:s.value},null,8,["loading"]),c("div",f,[a(l,{class:"md:w-1/3 w-full",loading:s.value},null,8,["loading"]),a(o,{class:"md:w-1/3 !md:mx-4 !md:my-0 !my-4 w-full",loading:s.value},null,8,["loading"]),a(n,{class:"md:w-1/3 w-full",loading:s.value},null,8,["loading"])])]))}});export{N as default}; 2 | -------------------------------------------------------------------------------- /src/api/sys/user.ts: -------------------------------------------------------------------------------- 1 | import { defHttp } from '/@/utils/http/axios'; 2 | import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel'; 3 | 4 | import { ErrorMessageMode } from '/#/axios'; 5 | 6 | enum Api { 7 | Login = '/login', 8 | Logout = '/logout', 9 | GetUserInfo = '/getUserInfo', 10 | GetPermCode = '/getPermCode', 11 | } 12 | 13 | /** 14 | * @description: user login api 15 | */ 16 | export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') { 17 | return defHttp.post( 18 | { 19 | url: Api.Login, 20 | params, 21 | }, 22 | { 23 | errorMessageMode: mode, 24 | }, 25 | ); 26 | } 27 | 28 | /** 29 | * @description: getUserInfo 30 | */ 31 | export function getUserInfo() { 32 | return defHttp.get({ url: Api.GetUserInfo }, { errorMessageMode: 'none' }); 33 | } 34 | 35 | export function getPermCode() { 36 | return defHttp.get({ url: Api.GetPermCode }); 37 | } 38 | 39 | export function doLogout() { 40 | return defHttp.get({ url: Api.Logout }); 41 | } 42 | -------------------------------------------------------------------------------- /dist/assets/index.a2505068.css: -------------------------------------------------------------------------------- 1 | .ant-breadcrumb{box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-variant:tabular-nums;line-height:1.5715;list-style:none;font-feature-settings:tnum;color:#00000073;font-size:14px}.ant-breadcrumb .anticon{font-size:14px}.ant-breadcrumb a{color:#00000073;transition:color .3s}.ant-breadcrumb a:hover{color:#2a7dc9}.ant-breadcrumb>span:last-child{color:#000000d9}.ant-breadcrumb>span:last-child a{color:#000000d9}.ant-breadcrumb>span:last-child .ant-breadcrumb-separator{display:none}.ant-breadcrumb-separator{margin:0 8px;color:#00000073}.ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-link>.anticon+a{margin-left:4px}.ant-breadcrumb-overlay-link>.anticon{margin-left:4px}.ant-breadcrumb-rtl{direction:rtl}.ant-breadcrumb-rtl:before,.ant-breadcrumb-rtl:after{display:table;content:""}.ant-breadcrumb-rtl:after{clear:both}.ant-breadcrumb-rtl>span{float:right}.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+span,.ant-breadcrumb-rtl .ant-breadcrumb-link>.anticon+a{margin-right:4px;margin-left:0}.ant-breadcrumb-rtl .ant-breadcrumb-overlay-link>.anticon{margin-right:4px;margin-left:0} 2 | -------------------------------------------------------------------------------- /src/components/Table/src/componentMap.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'vue'; 2 | import { 3 | Input, 4 | Select, 5 | Checkbox, 6 | InputNumber, 7 | Switch, 8 | DatePicker, 9 | TimePicker, 10 | } from 'ant-design-vue'; 11 | import type { ComponentType } from './types/componentType'; 12 | import { ApiSelect, ApiTreeSelect } from '/@/components/Form'; 13 | 14 | const componentMap = new Map(); 15 | 16 | componentMap.set('Input', Input); 17 | componentMap.set('InputNumber', InputNumber); 18 | componentMap.set('Select', Select); 19 | componentMap.set('ApiSelect', ApiSelect); 20 | componentMap.set('ApiTreeSelect', ApiTreeSelect); 21 | componentMap.set('Switch', Switch); 22 | componentMap.set('Checkbox', Checkbox); 23 | componentMap.set('DatePicker', DatePicker); 24 | componentMap.set('TimePicker', TimePicker); 25 | 26 | export function add(compName: ComponentType, component: Component) { 27 | componentMap.set(compName, component); 28 | } 29 | 30 | export function del(compName: ComponentType) { 31 | componentMap.delete(compName); 32 | } 33 | 34 | export { componentMap }; 35 | -------------------------------------------------------------------------------- /dist/assets/Exception.1646ace5.css: -------------------------------------------------------------------------------- 1 | .ant-result{padding:48px 32px}.ant-result-success .ant-result-icon>.anticon{color:#55d187}.ant-result-error .ant-result-icon>.anticon{color:#ed6f6f}.ant-result-info .ant-result-icon>.anticon{color:#0960bd}.ant-result-warning .ant-result-icon>.anticon{color:#efbd47}.ant-result-image{width:250px;height:295px;margin:auto}.ant-result-icon{margin-bottom:24px;text-align:center}.ant-result-icon>.anticon{font-size:72px}.ant-result-title{color:#000000d9;font-size:24px;line-height:1.8;text-align:center}.ant-result-subtitle{color:#00000073;font-size:14px;line-height:1.6;text-align:center}.ant-result-extra{margin:24px 0 0;text-align:center}.ant-result-extra>*{margin-right:8px}.ant-result-extra>*:last-child{margin-right:0}.ant-result-content{margin-top:24px;padding:24px 40px;background-color:#fafafa}.ant-result-rtl{direction:rtl}.ant-result-rtl .ant-result-extra>*{margin-right:0;margin-left:8px}.ant-result-rtl .ant-result-extra>*:last-child{margin-left:0}.vben-app-exception-page{display:flex;align-items:center;flex-direction:column}.vben-app-exception-page .ant-result-icon img{max-width:400px;max-height:300px} 2 | -------------------------------------------------------------------------------- /src/hooks/web/usePagination.ts: -------------------------------------------------------------------------------- 1 | import type { Ref } from 'vue'; 2 | import { ref, unref, computed } from 'vue'; 3 | 4 | function pagination(list: T[], pageNo: number, pageSize: number): T[] { 5 | const offset = (pageNo - 1) * Number(pageSize); 6 | const ret = 7 | offset + Number(pageSize) >= list.length 8 | ? list.slice(offset, list.length) 9 | : list.slice(offset, offset + Number(pageSize)); 10 | return ret; 11 | } 12 | 13 | export function usePagination(list: Ref, pageSize: number) { 14 | const currentPage = ref(1); 15 | const pageSizeRef = ref(pageSize); 16 | 17 | const getPaginationList = computed(() => { 18 | return pagination(unref(list), unref(currentPage), unref(pageSizeRef)); 19 | }); 20 | 21 | const getTotal = computed(() => { 22 | return unref(list).length; 23 | }); 24 | 25 | function setCurrentPage(page: number) { 26 | currentPage.value = page; 27 | } 28 | 29 | function setPageSize(pageSize: number) { 30 | pageSizeRef.value = pageSize; 31 | } 32 | 33 | return { setCurrentPage, getTotal, setPageSize, getPaginationList }; 34 | } 35 | -------------------------------------------------------------------------------- /src/components/Menu/src/components/MenuItemContent.vue: -------------------------------------------------------------------------------- 1 | 7 | 35 | -------------------------------------------------------------------------------- /src/components/Application/src/search/AppSearch.vue: -------------------------------------------------------------------------------- 1 | 34 | -------------------------------------------------------------------------------- /src/components/Modal/src/components/ModalFooter.vue: -------------------------------------------------------------------------------- 1 | 20 | 41 | -------------------------------------------------------------------------------- /dist/assets/ProjectCard.34a3184e.js: -------------------------------------------------------------------------------- 1 | import{A as m,bL as c,a0 as t,B as n,a1 as d,a5 as o,w as l,D as f,ao as x,H as s,J as a,aa as C,a4 as h,ad as j}from"./vendor.a45ea33e.js";/* empty css *//* empty css *//* empty css */import{I as g,_ as y}from"./index.c19540a9.js";import{g as k}from"./data.8ddc77c3.js";const v=m({components:{Card:c,CardGrid:c.Grid,Icon:g},setup(){return{items:k}}}),w=j("\u66F4\u591A"),B={class:"flex"},I={class:"text-lg ml-4"},$={class:"flex mt-2 h-10 text-secondary"},b={class:"flex justify-between text-secondary"};function G(r,E,N,V,z,A){const i=t("a-button"),p=t("Icon"),_=t("CardGrid"),u=t("Card");return n(),d(u,h({title:"\u9879\u76EE"},r.$attrs),{extra:o(()=>[l(i,{type:"link",size:"small"},{default:o(()=>[w]),_:1})]),default:o(()=>[(n(!0),f(C,null,x(r.items,e=>(n(),d(_,{key:e,class:"!md:w-1/3 !w-full"},{default:o(()=>[s("span",B,[l(p,{icon:e.icon,color:e.color,size:"30"},null,8,["icon","color"]),s("span",I,a(e.title),1)]),s("div",$,a(e.desc),1),s("div",b,[s("span",null,a(e.group),1),s("span",null,a(e.date),1)])]),_:2},1024))),128))]),_:1},16)}var S=y(v,[["render",G]]);export{S as default}; 2 | -------------------------------------------------------------------------------- /src/views/dashboard/workbench/components/DynamicInfo.vue: -------------------------------------------------------------------------------- 1 | 24 | 32 | -------------------------------------------------------------------------------- /tests/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "server", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "nodemon", 7 | "build": "rimraf ./dist && tsup ./index.ts --dts --format cjs,esm ", 8 | "prod": "npx pm2 start ecosystem.config.js --env production", 9 | "restart": "pm2 restart ecosystem.config.js --env production", 10 | "stop": "npx pm2 stop ecosystem.config.js" 11 | }, 12 | "dependencies": { 13 | "fs-extra": "^10.0.0", 14 | "koa": "^2.13.4", 15 | "koa-body": "^4.2.0", 16 | "koa-bodyparser": "^4.3.0", 17 | "koa-route": "^3.2.0", 18 | "koa-router": "^10.1.1", 19 | "koa-static": "^5.0.0", 20 | "koa-websocket": "^6.0.0", 21 | "koa2-cors": "^2.0.6" 22 | }, 23 | "devDependencies": { 24 | "@types/koa": "^2.13.4", 25 | "@types/koa-bodyparser": "^5.0.2", 26 | "@types/koa-router": "^7.4.4", 27 | "@types/node": "^16.11.6", 28 | "nodemon": "^2.0.14", 29 | "pm2": "^5.1.2", 30 | "rimraf": "^3.0.2", 31 | "ts-node": "^10.4.0", 32 | "tsconfig-paths": "^3.11.0", 33 | "tsup": "^5.5.0", 34 | "typescript": "^4.4.4" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/hooks/web/useTitle.ts: -------------------------------------------------------------------------------- 1 | import { watch, unref } from 'vue'; 2 | import { useI18n } from '/@/hooks/web/useI18n'; 3 | import { useTitle as usePageTitle } from '@vueuse/core'; 4 | import { useGlobSetting } from '/@/hooks/setting'; 5 | import { useRouter } from 'vue-router'; 6 | import { useLocaleStore } from '/@/store/modules/locale'; 7 | 8 | import { REDIRECT_NAME } from '/@/router/constant'; 9 | 10 | /** 11 | * Listening to page changes and dynamically changing site titles 12 | */ 13 | export function useTitle() { 14 | const { title } = useGlobSetting(); 15 | const { t } = useI18n(); 16 | const { currentRoute } = useRouter(); 17 | const localeStore = useLocaleStore(); 18 | 19 | const pageTitle = usePageTitle(); 20 | 21 | watch( 22 | [() => currentRoute.value.path, () => localeStore.getLocale], 23 | () => { 24 | const route = unref(currentRoute); 25 | 26 | if (route.name === REDIRECT_NAME) { 27 | return; 28 | } 29 | 30 | const tTitle = t(route?.meta?.title as string); 31 | pageTitle.value = tTitle ? ` ${tTitle} - ${title} ` : `${title}`; 32 | }, 33 | { immediate: true }, 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /src/hooks/core/useTimeout.ts: -------------------------------------------------------------------------------- 1 | import { ref, watch } from 'vue'; 2 | import { tryOnUnmounted } from '@vueuse/core'; 3 | import { isFunction } from '/@/utils/is'; 4 | 5 | export function useTimeoutFn(handle: Fn, wait: number, native = false) { 6 | if (!isFunction(handle)) { 7 | throw new Error('handle is not Function!'); 8 | } 9 | 10 | const { readyRef, stop, start } = useTimeoutRef(wait); 11 | if (native) { 12 | handle(); 13 | } else { 14 | watch( 15 | readyRef, 16 | (maturity) => { 17 | maturity && handle(); 18 | }, 19 | { immediate: false }, 20 | ); 21 | } 22 | return { readyRef, stop, start }; 23 | } 24 | 25 | export function useTimeoutRef(wait: number) { 26 | const readyRef = ref(false); 27 | 28 | let timer: TimeoutHandle; 29 | function stop(): void { 30 | readyRef.value = false; 31 | timer && window.clearTimeout(timer); 32 | } 33 | function start(): void { 34 | stop(); 35 | timer = setTimeout(() => { 36 | readyRef.value = true; 37 | }, wait); 38 | } 39 | 40 | start(); 41 | 42 | tryOnUnmounted(stop); 43 | 44 | return { readyRef, stop, start }; 45 | } 46 | -------------------------------------------------------------------------------- /.github/workflows/issue-labeled.yml: -------------------------------------------------------------------------------- 1 | name: Issue Labeled 2 | 3 | on: 4 | issues: 5 | types: [labeled] 6 | 7 | jobs: 8 | reply-labeled: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: remove pending 12 | if: github.event.label.name == 'enhancement' || github.event.label.name == 'bug' 13 | uses: actions-cool/issues-helper@v2.1.1 14 | with: 15 | actions: 'remove-labels' 16 | token: ${{ secrets.OPER_TOKEN }} 17 | issue-number: ${{ github.event.issue.number }} 18 | labels: 'bug: pending triage' 19 | 20 | - name: need reproduction 21 | if: github.event.label.name == 'need reproduction' 22 | uses: actions-cool/issues-helper@v2.1.1 23 | with: 24 | actions: 'create-comment, remove-labels' 25 | token: ${{ secrets.OPER_TOKEN }} 26 | issue-number: ${{ github.event.issue.number }} 27 | body: | 28 | Hello @${{ github.event.issue.user.login }}. Please provide the complete reproduction steps and code. Issues labeled by `need reproduction` will be closed if no activities in 3 days. 29 | labels: 'bug: pending triage' 30 | -------------------------------------------------------------------------------- /dist/assets/SessionTimeoutLogin.bf633ee8.js: -------------------------------------------------------------------------------- 1 | import{A as n,r as m,_ as d,aj as c,B as u,a1 as p,a5 as l,H as _,w as f,K as j,u as g,a2 as x}from"./vendor.a45ea33e.js";import v from"./Login.c4235e02.js";import{_ as B,a as S,f as C,J as F,V as I,aW as M}from"./index.c19540a9.js";import"./LoginForm.d9832a79.js";/* empty css *//* empty css *//* empty css *//* empty css */import"./LoginFormTitle.e74ca14b.js";import"./ForgetPasswordForm.e2eb631c.js";import"./index.402cae56.js";import"./RegisterForm.cd19c0cd.js";import"./index.a241463b.js";import"./MobileForm.b08af510.js";import"./QrCodeForm.5343d43d.js";import"./download.29a0bc39.js";const b=n({setup(L){const{prefixCls:r}=S("st-login"),e=C(),t=F(),a=I(),o=m(0),i=()=>a.getProjectConfig.permissionMode===M.BACK;return d(()=>{var s;o.value=(s=e.getUserInfo)==null?void 0:s.userId,console.log("Mounted",e.getUserInfo)}),c(()=>{(o.value&&o.value!==e.getUserInfo.userId||i()&&t.getLastBuildMenuTime===0)&&document.location.reload()}),(s,T)=>(u(),p(x,null,{default:l(()=>[_("div",{class:j(g(r))},[f(v,{sessionTimeout:""})],2)]),_:1}))}});var R=B(b,[["__scopeId","data-v-6fac6116"]]);export{R as default}; 2 | -------------------------------------------------------------------------------- /src/api/bot/model/job.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * job.Job,添加任务的入参 3 | */ 4 | export interface Job { 5 | /** 6 | * 回答类型, jobType=3使用的参数, 1=文本消息, 2=注入消息 7 | */ 8 | answerType: number; 9 | /** 10 | * 指令别名类型, jobType=1使用的参数, 1=无状态消息, 2=主人消息 11 | */ 12 | fullMatchType: number; 13 | /** 14 | * 群聊id, jobType=2,3使用的参数, jobType=2且私聊, group_id=0 15 | */ 16 | groupId: number; 17 | /** 18 | * 执行内容 19 | */ 20 | handler: string; 21 | /** 22 | * 任务id 23 | */ 24 | id?: string; 25 | /** 26 | * 任务类型,1-指令别名,2-定时任务,3-你问我答 27 | */ 28 | jobType: number; 29 | /** 30 | * 当jobType=1时 为指令别名,当jobType=2时 为cron表达式,当jobType=3时 为正则表达式 31 | */ 32 | matcher: string; 33 | /** 34 | * 问题类型, jobType=3使用的参数, 1=单独问, 2=所有人问 35 | */ 36 | questionType: number; 37 | /** 38 | * 机器人id 39 | */ 40 | selfId?: number; 41 | /** 42 | * 用户id, jobType=2,3使用的参数, 当jobType=3, QuestionType=2,userId=0 43 | */ 44 | userId: number; 45 | } 46 | 47 | /** 48 | * job.DeleteReq,删除任务的入参 49 | */ 50 | export interface DeleteReq { 51 | /** 52 | * 任务id 53 | */ 54 | idList: string[]; 55 | /** 56 | * 机器人qq 57 | */ 58 | selfId: number; 59 | } 60 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # Whether to open mock 2 | VITE_USE_MOCK = false 3 | 4 | # public path 5 | VITE_PUBLIC_PATH = / 6 | 7 | # Cross-domain proxy, you can configure multiple 8 | # Please note that no line breaks 9 | # VITE_PROXY = [["/basic-api","http://localhost:3000/api"],["/upload","http://localhost:3300/upload"],["/api","http://localhost:3000/api"]] 10 | 11 | # Delete console 12 | VITE_DROP_CONSOLE = true 13 | 14 | # Whether to enable gzip or brotli compression 15 | # Optional: gzip | brotli | none 16 | # If you need multiple forms, you can use `,` to separate 17 | VITE_BUILD_COMPRESS = 'none' 18 | 19 | # Whether to delete origin files when using compress, default false 20 | VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE = false 21 | 22 | # Basic interface address SPA 23 | VITE_GLOB_API_URL=/api 24 | 25 | # File upload address, optional 26 | # It can be forwarded by nginx or write the actual address directly 27 | VITE_GLOB_UPLOAD_URL=/upload 28 | 29 | # Interface prefix 30 | VITE_GLOB_API_URL_PREFIX= 31 | 32 | # Whether to enable image compression 33 | VITE_USE_IMAGEMIN= true 34 | 35 | # use pwa 36 | VITE_USE_PWA = false 37 | 38 | # Is it compatible with older browsers 39 | VITE_LEGACY = false 40 | -------------------------------------------------------------------------------- /dist/assets/index.88b1d373.css: -------------------------------------------------------------------------------- 1 | .vben-strength-meter-bar[data-v-3bfd49a1]{position:relative;height:6px;margin:10px auto 6px;background-color:#00000040;border-radius:6px}.vben-strength-meter-bar[data-v-3bfd49a1]:before,.vben-strength-meter-bar[data-v-3bfd49a1]:after{position:absolute;z-index:10;display:block;width:20%;height:inherit;background-color:transparent;border-color:#fff;border-style:solid;border-width:0 5px;content:""}.vben-strength-meter-bar[data-v-3bfd49a1]:before{left:20%}.vben-strength-meter-bar[data-v-3bfd49a1]:after{right:20%}.vben-strength-meter-bar--fill[data-v-3bfd49a1]{position:absolute;width:0;height:inherit;background-color:transparent;border-radius:inherit;transition:width .5s ease-in-out,background .25s}.vben-strength-meter-bar--fill[data-score="0"][data-v-3bfd49a1]{width:20%;background-color:#e74242}.vben-strength-meter-bar--fill[data-score="1"][data-v-3bfd49a1]{width:40%;background-color:#ed6f6f}.vben-strength-meter-bar--fill[data-score="2"][data-v-3bfd49a1]{width:60%;background-color:#efbd47}.vben-strength-meter-bar--fill[data-score="3"][data-v-3bfd49a1]{width:80%;background-color:#55d18780}.vben-strength-meter-bar--fill[data-score="4"][data-v-3bfd49a1]{width:100%;background-color:#55d187} 2 | -------------------------------------------------------------------------------- /src/design/transition/scroll.less: -------------------------------------------------------------------------------- 1 | .scroll-y-transition { 2 | .transition-default(); 3 | 4 | &-enter-from, 5 | &-leave-to { 6 | opacity: 0%; 7 | } 8 | 9 | &-enter-from { 10 | transform: translateY(-15px); 11 | } 12 | 13 | &-leave-to { 14 | transform: translateY(15px); 15 | } 16 | } 17 | 18 | .scroll-y-reverse-transition { 19 | .transition-default(); 20 | 21 | &-enter-from, 22 | &-leave-to { 23 | opacity: 0%; 24 | } 25 | 26 | &-enter-from { 27 | transform: translateY(15px); 28 | } 29 | 30 | &-leave-to { 31 | transform: translateY(-15px); 32 | } 33 | } 34 | 35 | .scroll-x-transition { 36 | .transition-default(); 37 | 38 | &-enter-from, 39 | &-leave-to { 40 | opacity: 0%; 41 | } 42 | 43 | &-enter-from { 44 | transform: translateX(-15px); 45 | } 46 | 47 | &-leave-to { 48 | transform: translateX(15px); 49 | } 50 | } 51 | 52 | .scroll-x-reverse-transition { 53 | .transition-default(); 54 | 55 | &-enter-from, 56 | &-leave-to { 57 | opacity: 0%; 58 | } 59 | 60 | &-enter-from { 61 | transform: translateX(15px); 62 | } 63 | 64 | &-leave-to { 65 | transform: translateX(-15px); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/utils/lib/echarts.ts: -------------------------------------------------------------------------------- 1 | import * as echarts from 'echarts/core'; 2 | 3 | import { 4 | BarChart, 5 | LineChart, 6 | PieChart, 7 | MapChart, 8 | PictorialBarChart, 9 | RadarChart, 10 | } from 'echarts/charts'; 11 | 12 | import { 13 | TitleComponent, 14 | TooltipComponent, 15 | GridComponent, 16 | PolarComponent, 17 | AriaComponent, 18 | ParallelComponent, 19 | LegendComponent, 20 | RadarComponent, 21 | ToolboxComponent, 22 | DataZoomComponent, 23 | VisualMapComponent, 24 | TimelineComponent, 25 | CalendarComponent, 26 | GraphicComponent, 27 | } from 'echarts/components'; 28 | 29 | import { SVGRenderer } from 'echarts/renderers'; 30 | 31 | echarts.use([ 32 | LegendComponent, 33 | TitleComponent, 34 | TooltipComponent, 35 | GridComponent, 36 | PolarComponent, 37 | AriaComponent, 38 | ParallelComponent, 39 | BarChart, 40 | LineChart, 41 | PieChart, 42 | MapChart, 43 | RadarChart, 44 | SVGRenderer, 45 | PictorialBarChart, 46 | RadarComponent, 47 | ToolboxComponent, 48 | DataZoomComponent, 49 | VisualMapComponent, 50 | TimelineComponent, 51 | CalendarComponent, 52 | GraphicComponent, 53 | ]); 54 | 55 | export default echarts; 56 | -------------------------------------------------------------------------------- /types/store.d.ts: -------------------------------------------------------------------------------- 1 | import { ErrorTypeEnum } from '/@/enums/exceptionEnum'; 2 | import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum'; 3 | import { RoleInfo } from '/@/api/sys/model/userModel'; 4 | 5 | // Lock screen information 6 | export interface LockInfo { 7 | // Password required 8 | pwd?: string | undefined; 9 | // Is it locked? 10 | isLock?: boolean; 11 | } 12 | 13 | // Error-log information 14 | export interface ErrorLogInfo { 15 | // Type of error 16 | type: ErrorTypeEnum; 17 | // Error file 18 | file: string; 19 | // Error name 20 | name?: string; 21 | // Error message 22 | message: string; 23 | // Error stack 24 | stack?: string; 25 | // Error detail 26 | detail: string; 27 | // Error url 28 | url: string; 29 | // Error time 30 | time?: string; 31 | } 32 | 33 | export interface UserInfo { 34 | userId: string | number; 35 | username: string; 36 | realName: string; 37 | avatar: string; 38 | desc?: string; 39 | homePath?: string; 40 | roles: RoleInfo[]; 41 | } 42 | 43 | export interface BeforeMiniState { 44 | menuCollapsed?: boolean; 45 | menuSplit?: boolean; 46 | menuMode?: MenuModeEnum; 47 | menuType?: MenuTypeEnum; 48 | } 49 | -------------------------------------------------------------------------------- /dist/assets/VisitAnalysisBar.5e71d8fc.js: -------------------------------------------------------------------------------- 1 | var p=Object.defineProperty;var o=Object.getOwnPropertySymbols;var l=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable;var i=(a,e,t)=>e in a?p(a,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):a[e]=t,u=(a,e)=>{for(var t in e||(e={}))l.call(e,t)&&i(a,t,e[t]);if(o)for(var t of o(e))c.call(e,t)&&i(a,t,e[t]);return a};import{u as h}from"./useECharts.9a72bc67.js";import{b as m}from"./props.f48aca0b.js";import{A as d,r as f,_ as b,B as x,D as y,X as g}from"./vendor.a45ea33e.js";import"./index.c19540a9.js";const C=d({props:u({},m),setup(a){const e=f(null),{setOptions:t}=h(e);return b(()=>{t({tooltip:{trigger:"axis",axisPointer:{lineStyle:{width:1,color:"#019680"}}},grid:{left:"1%",right:"1%",top:"2 %",bottom:0,containLabel:!0},xAxis:{type:"category",data:["1\u6708","2\u6708","3\u6708","4\u6708","5\u6708","6\u6708","7\u6708","8\u6708","9\u6708","10\u6708","11\u6708","12\u6708"]},yAxis:{type:"value",max:8e3,splitNumber:4},series:[{data:[3e3,2e3,3333,5e3,3200,4200,3200,2100,3e3,5100,6e3,3200,4800],type:"bar",barMaxWidth:80}]})}),(r,j)=>(x(),y("div",{ref:(s,n)=>{n.chartRef=s,e.value=s},style:g({height:r.height,width:r.width})},null,4))}});export{C as default}; 2 | -------------------------------------------------------------------------------- /src/hooks/web/useScript.ts: -------------------------------------------------------------------------------- 1 | import { onMounted, onUnmounted, ref } from 'vue'; 2 | 3 | interface ScriptOptions { 4 | src: string; 5 | } 6 | 7 | export function useScript(opts: ScriptOptions) { 8 | const isLoading = ref(false); 9 | const error = ref(false); 10 | const success = ref(false); 11 | let script: HTMLScriptElement; 12 | 13 | const promise = new Promise((resolve, reject) => { 14 | onMounted(() => { 15 | script = document.createElement('script'); 16 | script.type = 'text/javascript'; 17 | script.onload = function () { 18 | isLoading.value = false; 19 | success.value = true; 20 | error.value = false; 21 | resolve(''); 22 | }; 23 | 24 | script.onerror = function (err) { 25 | isLoading.value = false; 26 | success.value = false; 27 | error.value = true; 28 | reject(err); 29 | }; 30 | 31 | script.src = opts.src; 32 | document.head.appendChild(script); 33 | }); 34 | }); 35 | 36 | onUnmounted(() => { 37 | script && script.remove(); 38 | }); 39 | 40 | return { 41 | isLoading, 42 | error, 43 | success, 44 | toPromise: () => promise, 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /dist/assets/SalesProductPie.82502710.js: -------------------------------------------------------------------------------- 1 | import{A as s,r as l,S as d,B as c,a1 as m,a5 as p,H as f,X as h,u as g,bL as B}from"./vendor.a45ea33e.js";/* empty css *//* empty css *//* empty css */import{u as x}from"./useECharts.9a72bc67.js";import"./index.c19540a9.js";const b=s({props:{loading:Boolean,width:{type:String,default:"100%"},height:{type:String,default:"300px"}},setup(e){const a=e,t=l(null),{setOptions:r}=x(t);return d(()=>a.loading,()=>{a.loading||r({tooltip:{trigger:"item"},series:[{name:"\u8BBF\u95EE\u6765\u6E90",type:"pie",radius:"80%",center:["50%","50%"],color:["#5ab1ef","#b6a2de","#67e0e3","#2ec7c9"],data:[{value:500,name:"\u7535\u5B50\u4EA7\u54C1"},{value:310,name:"\u670D\u88C5"},{value:274,name:"\u5316\u5986\u54C1"},{value:400,name:"\u5BB6\u5C45"}].sort(function(n,i){return n.value-i.value}),roseType:"radius",animationType:"scale",animationEasing:"exponentialInOut",animationDelay:function(){return Math.random()*400}}]})},{immediate:!0}),(n,i)=>(c(),m(g(B),{title:"\u6210\u4EA4\u5360\u6BD4",loading:e.loading},{default:p(()=>[f("div",{ref:(u,o)=>{o.chartRef=u,t.value=u},style:h({width:e.width,height:e.height})},null,4)]),_:1},8,["loading"]))}});export{b as default}; 2 | -------------------------------------------------------------------------------- /src/design/public.less: -------------------------------------------------------------------------------- 1 | #app { 2 | width: 100%; 3 | height: 100%; 4 | } 5 | 6 | // ================================= 7 | // ==============scrollbar========== 8 | // ================================= 9 | 10 | ::-webkit-scrollbar { 11 | width: 7px; 12 | height: 8px; 13 | } 14 | 15 | // ::-webkit-scrollbar-track { 16 | // background: transparent; 17 | // } 18 | 19 | ::-webkit-scrollbar-track { 20 | background-color: rgb(0 0 0 / 5%); 21 | } 22 | 23 | ::-webkit-scrollbar-thumb { 24 | // background: rgba(0, 0, 0, 0.6); 25 | background-color: rgb(144 147 153 / 30%); 26 | // background-color: rgba(144, 147, 153, 0.3); 27 | border-radius: 2px; 28 | box-shadow: inset 0 0 6px rgb(0 0 0 / 20%); 29 | } 30 | 31 | ::-webkit-scrollbar-thumb:hover { 32 | background-color: @border-color-dark; 33 | } 34 | 35 | // ================================= 36 | // ==============nprogress========== 37 | // ================================= 38 | #nprogress { 39 | pointer-events: none; 40 | 41 | .bar { 42 | position: fixed; 43 | top: 0; 44 | left: 0; 45 | z-index: 99999; 46 | width: 100%; 47 | height: 2px; 48 | background-color: @primary-color; 49 | opacity: 75%; 50 | } 51 | } 52 | --------------------------------------------------------------------------------