From f0c4f1f39e5fb9eea9837dacbb6e4c702ed25a38 Mon Sep 17 00:00:00 2001 From: Truong Nguyen Date: Wed, 10 Jul 2024 18:08:06 +0700 Subject: [PATCH] update --- .env | 4 +- .env.development | 6 +- .env.production | 14 +- .env.staging | 14 +- .vscode/settings.json | 2 +- README.en-US.md | 2 +- README.md | 46 +- locales/en.yaml | 147 ++- locales/vi.yaml | 238 +++++ locales/zh-CN.yaml | 89 -- mock/asyncRoutes.ts | 2 + package.json | 14 +- pnpm-lock.yaml | 875 +++++++++++++++++- public/platform-config.json | 2 +- src/App.vue | 4 +- src/api/routes.ts | 4 +- src/api/user.ts | 24 +- src/components/ReCol/index.ts | 2 +- src/components/ReCountTo/README.md | 2 + src/components/ReCountTo/index.ts | 11 + src/components/ReCountTo/src/normal/index.tsx | 179 ++++ src/components/ReCountTo/src/normal/props.ts | 32 + .../ReCountTo/src/rebound/index.tsx | 72 ++ src/components/ReCountTo/src/rebound/props.ts | 15 + .../ReCountTo/src/rebound/rebound.css | 77 ++ src/components/ReDialog/index.ts | 16 +- src/components/ReDialog/index.vue | 4 +- src/components/ReFlicker/index.css | 39 + src/components/ReFlicker/index.ts | 44 + src/components/ReIcon/index.ts | 6 +- src/components/ReIcon/src/hooks.ts | 12 +- src/components/ReIcon/src/iconfont.ts | 2 +- .../ReIcon/src/iconifyIconOffline.ts | 2 +- .../ReIcon/src/iconifyIconOnline.ts | 2 +- src/components/ReIcon/src/offlineIcon.ts | 6 +- src/components/ReIcon/src/types.ts | 2 +- src/components/RePureTableBar/index.ts | 2 +- src/components/RePureTableBar/src/bar.tsx | 32 +- src/components/ReSegmented/index.ts | 2 +- src/components/ReSegmented/src/index.tsx | 10 +- src/components/ReSegmented/src/type.ts | 14 +- src/components/ReText/index.ts | 2 +- src/components/ReText/src/index.vue | 6 +- src/config/index.ts | 10 +- src/directives/copy/index.ts | 10 +- src/directives/optimize/index.ts | 20 +- src/layout/components/lay-content/index.vue | 2 +- src/layout/components/lay-navbar/index.vue | 22 +- .../components/lay-sidebar/NavHorizontal.vue | 20 +- src/layout/components/lay-sidebar/NavMix.vue | 22 +- src/layout/hooks/useDataThemeChange.ts | 26 +- src/main.ts | 68 +- src/plugins/i18n.ts | 38 +- src/plugins/vxeTable.ts | 103 +++ src/router/utils.ts | 84 +- src/store/modules/app.ts | 4 +- src/style/reset.scss | 17 +- src/utils/auth.ts | 64 +- src/utils/globalPolyfills.ts | 3 +- src/utils/http/index.ts | 45 +- src/utils/localforage/index.ts | 24 +- src/utils/message.ts | 30 +- src/utils/mitt.ts | 9 +- src/utils/preventDefault.ts | 12 +- src/utils/progress/index.ts | 10 +- src/utils/propTypes.ts | 2 +- src/utils/responsive.ts | 42 +- src/utils/sso.ts | 28 +- src/utils/tree.ts | 52 +- src/views/login/index.vue | 20 +- .../welcome/components/charts/ChartBar.vue | 111 +++ .../welcome/components/charts/ChartLine.vue | 62 ++ .../welcome/components/charts/ChartRound.vue | 73 ++ src/views/welcome/components/charts/index.ts | 3 + .../welcome/components/table/columns.tsx | 104 +++ src/views/welcome/components/table/empty.svg | 1 + src/views/welcome/components/table/index.vue | 71 ++ src/views/welcome/data.ts | 142 +++ src/views/welcome/index.vue | 269 +++++- src/views/welcome/utils.ts | 6 + types/directives.d.ts | 18 +- types/global-components.d.ts | 4 +- types/global.d.ts | 31 +- types/index.d.ts | 2 +- types/router.d.ts | 76 +- vite.config.ts | 12 +- 86 files changed, 3228 insertions(+), 624 deletions(-) create mode 100644 locales/vi.yaml delete mode 100644 locales/zh-CN.yaml create mode 100644 src/components/ReCountTo/README.md create mode 100644 src/components/ReCountTo/index.ts create mode 100644 src/components/ReCountTo/src/normal/index.tsx create mode 100644 src/components/ReCountTo/src/normal/props.ts create mode 100644 src/components/ReCountTo/src/rebound/index.tsx create mode 100644 src/components/ReCountTo/src/rebound/props.ts create mode 100644 src/components/ReCountTo/src/rebound/rebound.css create mode 100644 src/components/ReFlicker/index.css create mode 100644 src/components/ReFlicker/index.ts create mode 100644 src/plugins/vxeTable.ts create mode 100644 src/views/welcome/components/charts/ChartBar.vue create mode 100644 src/views/welcome/components/charts/ChartLine.vue create mode 100644 src/views/welcome/components/charts/ChartRound.vue create mode 100644 src/views/welcome/components/charts/index.ts create mode 100644 src/views/welcome/components/table/columns.tsx create mode 100644 src/views/welcome/components/table/empty.svg create mode 100644 src/views/welcome/components/table/index.vue create mode 100644 src/views/welcome/data.ts create mode 100644 src/views/welcome/utils.ts diff --git a/.env b/.env index 09344c1..f44e734 100644 --- a/.env +++ b/.env @@ -1,5 +1,5 @@ -# 平台本地运行端口号 +# Cổng chạy cục bộ nền tảng VITE_PORT = 8848 -# 是否隐藏首页 隐藏 true 不隐藏 false (勿删除,VITE_HIDE_HOME只需在.env文件配置) +# Có ẩn trang chủ hay không: ẩn true, không ẩn false (Đừng xóa, VITE_HIDE_HOME chỉ cần cấu hình trong tệp .env) VITE_HIDE_HOME = false diff --git a/.env.development b/.env.development index 90d1146..bafcf74 100644 --- a/.env.development +++ b/.env.development @@ -1,8 +1,8 @@ -# 平台本地运行端口号 +# Cổng chạy cục bộ nền tảng VITE_PORT = 8848 -# 开发环境读取配置文件路径 +# Đường dẫn đọc tệp cấu hình môi trường phát triển VITE_PUBLIC_PATH = / -# 开发环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +# Chế độ lịch sử của router môi trường phát triển (Chế độ Hash truyền "hash", Chế độ HTML5 truyền "h5", Chế độ Hash với tham số base truyền "hash,base", Chế độ HTML5 với tham số base truyền "h5,base") VITE_ROUTER_HISTORY = "hash" diff --git a/.env.production b/.env.production index 84e6086..30a61a2 100644 --- a/.env.production +++ b/.env.production @@ -1,13 +1,13 @@ -# 线上环境平台打包路径 +# Đường dẫn đóng gói nền tảng môi trường trực tuyến VITE_PUBLIC_PATH = / -# 线上环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +# Chế độ lịch sử của router môi trường trực tuyến (Chế độ Hash truyền "hash", Chế độ HTML5 truyền "h5", Chế độ Hash với tham số base truyền "hash,base", Chế độ HTML5 với tham số base truyền "h5,base") VITE_ROUTER_HISTORY = "hash" -# 是否在打包时使用cdn替换本地库 替换 true 不替换 false +# Có sử dụng CDN để thay thế thư viện địa phương khi đóng gói hay không: thay thế true, không thay thế false VITE_CDN = false -# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) -# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) -# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) -VITE_COMPRESSION = "none" \ No newline at end of file +# Có bật nén gzip hoặc nén brotli hay không (chia thành hai trường hợp, xóa tệp gốc và không xóa tệp gốc) +# Cấu hình không xóa tệp gốc khi nén: gzip, brotli, both (bật đồng thời nén gzip và brotli), none (không bật nén, mặc định) +# Cấu hình xóa tệp gốc khi nén: gzip-clear, brotli-clear, both-clear (bật đồng thời nén gzip và brotli), none (không bật nén, mặc định) +VITE_COMPRESSION = "none" diff --git a/.env.staging b/.env.staging index 65b57e3..1bfae84 100644 --- a/.env.staging +++ b/.env.staging @@ -1,16 +1,16 @@ -# 预发布也需要生产环境的行为 -# https://cn.vitejs.dev/guide/env-and-mode.html#modes +# Pre-release cũng cần hành vi của môi trường sản xuất +# https://vitejs.dev/guide/env-and-mode.html#modes # NODE_ENV = development VITE_PUBLIC_PATH = / -# 预发布环境路由历史模式(Hash模式传"hash"、HTML5模式传"h5"、Hash模式带base参数传"hash,base参数"、HTML5模式带base参数传"h5,base参数") +# Chế độ lịch sử của router môi trường pre-release (Chế độ Hash truyền "hash", Chế độ HTML5 truyền "h5", Chế độ Hash với tham số base truyền "hash,base", Chế độ HTML5 với tham số base truyền "h5,base") VITE_ROUTER_HISTORY = "hash" -# 是否在打包时使用cdn替换本地库 替换 true 不替换 false +# Có sử dụng CDN để thay thế thư viện địa phương khi đóng gói hay không: thay thế true, không thay thế false VITE_CDN = true -# 是否启用gzip压缩或brotli压缩(分两种情况,删除原始文件和不删除原始文件) -# 压缩时不删除原始文件的配置:gzip、brotli、both(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) -# 压缩时删除原始文件的配置:gzip-clear、brotli-clear、both-clear(同时开启 gzip 与 brotli 压缩)、none(不开启压缩,默认) +# Có bật nén gzip hoặc nén brotli hay không (chia thành hai trường hợp, xóa tệp gốc và không xóa tệp gốc) +# Cấu hình không xóa tệp gốc khi nén: gzip, brotli, both (bật đồng thời nén gzip và brotli), none (không bật nén, mặc định) +# Cấu hình xóa tệp gốc khi nén: gzip-clear, brotli-clear, both-clear (bật đồng thời nén gzip và brotli), none (không bật nén, mặc định) VITE_COMPRESSION = "none" diff --git a/.vscode/settings.json b/.vscode/settings.json index 59f3f0b..f5143bf 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,7 +36,7 @@ "js" ], "i18n-ally.sourceLanguage": "en", - "i18n-ally.displayLanguage": "zh-CN", + "i18n-ally.displayLanguage": "vi", "i18n-ally.enabledFrameworks": [ "vue" ], diff --git a/README.en-US.md b/README.en-US.md index b8289da..e09d65f 100644 --- a/README.en-US.md +++ b/README.en-US.md @@ -2,7 +2,7 @@ [![license](https://img.shields.io/github/license/pure-admin/vue-pure-admin.svg)](LICENSE) -**English** | [中文](./README.md) +**English** | [TIẾNG VIỆT](./README.md) ## Introduce diff --git a/README.md b/README.md index f19a805..99dfe82 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,51 @@ -

vue-pure-admin精简版(国际化版本)

+

vue-pure-admin phiên bản tinh gọn (phiên bản quốc tế hóa)

[![license](https://img.shields.io/github/license/pure-admin/vue-pure-admin.svg)](LICENSE) **中文** | [English](./README.en-US.md) -## 介绍 +## Giới thiệu -精简版是基于 [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin) 提炼出的架子,包含主体功能,更适合实际项目开发,打包后的大小在全局引入 [element-plus](https://element-plus.org) 的情况下仍然低于 `2.3MB`,并且会永久同步完整版的代码。开启 `brotli` 压缩和 `cdn` 替换本地库模式后,打包大小低于 `350kb` +Phiên bản tinh gọn được trích xuất từ [vue-pure-admin](https://github.com/pure-admin/vue-pure-admin), chứa các chức năng chính, phù hợp hơn cho việc phát triển dự án thực tế. Kích thước gói sau khi đóng gói, khi nhập toàn cục [element-plus](https://element-plus.org) vẫn dưới `2.3MB` và sẽ luôn đồng bộ mã với phiên bản đầy đủ. Khi bật nén `brotli` và thay thế thư viện địa phương bằng `cdn`, kích thước gói đóng gói sẽ dưới `350kb`. -## 版本选择 +## Lựa chọn phiên bản -当前是国际化版本,如果您需要非国际化版本 [请点击](https://github.com/pure-admin/pure-admin-thin) +Hiện tại là phiên bản quốc tế hóa, nếu bạn cần phiên bản không quốc tế hóa [hãy nhấp vào đây](https://github.com/pure-admin/pure-admin-thin). -## `js` 版本 +## Phiên bản `js` -[点我查看 js 版本](https://pure-admin.github.io/pure-admin-doc/pages/js/) +[Nhấp vào đây để xem phiên bản js](https://pure-admin.github.io/pure-admin-doc/pages/js/) -## `max` 版本 +## Phiên bản `max` -[点我查看 max 版本](https://github.com/pure-admin/vue-pure-admin-max) +[Nhấp vào đây để xem phiên bản max](https://github.com/pure-admin/vue-pure-admin-max) -## 配套视频 +## Video hướng dẫn kèm theo -[点我查看 UI 设计](https://www.bilibili.com/video/BV17g411T7rq) -[点我查看快速开发教程](https://www.bilibili.com/video/BV1kg411v7QT) +[Nhấp vào đây để xem thiết kế UI](https://www.bilibili.com/video/BV17g411T7rq) +[Nhấp vào đây để xem hướng dẫn phát triển nhanh](https://www.bilibili.com/video/BV1kg411v7QT) -## 配套保姆级文档 +## Tài liệu hướng dẫn kèm theo -[点我查看 vue-pure-admin 文档](https://pure-admin.github.io/pure-admin-doc) -[点我查看 @pureadmin/utils 文档](https://pure-admin-utils.netlify.app) +[Nhấp vào đây để xem tài liệu vue-pure-admin](https://pure-admin.github.io/pure-admin-doc) +[Nhấp vào đây để xem tài liệu @pureadmin/utils](https://pure-admin-utils.netlify.app) -## 优质服务、软件外包、赞助支持 +## Dịch vụ chất lượng, gia công phần mềm, hỗ trợ tài trợ -[点我查看详情](https://pure-admin.github.io/pure-admin-doc/pages/service/) +[Nhấp vào đây để xem chi tiết](https://pure-admin.github.io/pure-admin-doc/pages/service/) -## 预览 +## Xem trước -[查看预览](https://pure-admin-thin.netlify.app/#/login) +[Xem trước](https://pure-admin-thin.netlify.app/#/login) -## 维护者 +## Người bảo trì [xiaoxian521](https://github.com/xiaoxian521) -## ⚠️ 注意 +## ⚠️ Chú ý -精简版不接受任何 `issues` 和 `pr`,如果有问题请到完整版 [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) 去提,谢谢! +Phiên bản tinh gọn không chấp nhận bất kỳ `issues` và `pr`, nếu có vấn đề xin vui lòng đến phiên bản đầy đủ [issues](https://github.com/pure-admin/vue-pure-admin/issues/new/choose) để đề xuất, cảm ơn! -## 许可证 +## Giấy phép [MIT © 2020-present, pure-admin](./LICENSE) diff --git a/locales/en.yaml b/locales/en.yaml index 0f3ed6f..d02f36f 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -1,4 +1,5 @@ buttons: + pureAccountSettings: Account pureLoginOut: LoginOut pureLogin: Login pureOpenSystemSet: Open System Configs @@ -63,13 +64,124 @@ panel: menus: pureHome: Home pureLogin: Login + pureEmpty: Empty Page + pureTable: Table + pureSysManagement: System Manage + pureUser: User Manage + pureRole: Role Manage + pureSystemMenu: Menu Manage + pureDept: Dept Manage + pureSysMonitor: System Monitor + pureOnlineUser: Online User + pureLoginLog: Login Log + pureOperationLog: Operation Log + pureSystemLog: System Log + pureEditor: Editor pureAbnormal: Abnormal Page pureFourZeroFour: "404" pureFourZeroOne: "403" pureFive: "500" + pureComponents: Components + pureDialog: Dialog + pureMessage: Message Tips + pureVideo: Video + pureSegmented: Segmented + pureWaterfall: Waterfall + pureMap: Map + pureDraggable: Draggable + pureSplitPane: Split Pane + pureText: Text Ellipsis + pureElButton: Button + pureButton: Button Animation + pureCheckButton: Check Button + pureCropping: Picture Cropping + pureAnimatecss: AnimateCss Selector + pureCountTo: Digital Animation + pureSelector: Scope Selector + pureFlowChart: Flow Chart + pureSeamless: Seamless Scroll + pureContextmenu: Context Menu + pureTypeit: Typeit + pureJsonEditor: JSON Editor + pureColorPicker: Color Picker + pureDatePicker: Date Picker + pureDateTimePicker: DateTimePicker + pureTimePicker: TimePicker + pureTag: Tag + pureStatistic: Statistic + pureCollapse: Collapse + pureGanttastic: Gantt Chart + pureProgress: Progress + pureUpload: File Upload + pureCheckCard: CheckCard + pureMenus: MultiLevel Menu + pureMenu1: Menu1 + pureMenu1-1: Menu1-1 + pureMenu1-2: Menu1-2 + pureMenu1-2-1: Menu1-2-1 + pureMenu1-2-2: Menu1-2-2 + pureMenu1-3: Menu1-3 + pureMenu2: Menu2 purePermission: Permission Manage purePermissionPage: Page Permission purePermissionButton: Button Permission + pureTabs: Tabs Operate + pureGuide: Guide + pureAble: Able + pureMenuTree: Menu Tree + pureVideoFrame: Video Frame Capture + pureWavesurfer: Audio Visualization + pureRipple: Ripple + pureMqtt: Mqtt Client + pureOptimize: Debounce、Throttle、Copy、Longpress Directives + pureVerify: Captcha + pureWatermark: Water Mark + purePrint: Print + pureDownload: Download + pureExternalPage: External Page + pureExternalDoc: Docs External + pureEmbeddedDoc: Docs Embedded + pureExternalLink: Vue-Pure-Admin + pureUtilsLink: Pure-Admin-Utils + pureColorHuntDoc: ColorHunt + pureUiGradients: UiGradients + pureEpDoc: Element-Plus + pureTailwindcssDoc: Tailwindcss + pureVueDoc: Vue3 + pureViteDoc: Vite + purePiniaDoc: Pinia + pureRouterDoc: Vue-Router + pureAbout: About + pureResult: Result Page + pureSuccess: Success Page + pureFail: Fail Page + pureIconSelect: Icon Select + pureTimeline: Time Line + pureLineTree: LineTree + pureList: List Page + pureCardList: Card List Page + pureDebounce: Debounce & Throttle + pureFormDesign: Form Design + pureBarcode: Barcode + pureQrcode: Qrcode + pureCascader: Area Cascader + pureSwiper: Swiper Plugin + pureVirtualList: Virtual List + purePdf: PDF Preview + pureExcel: Export Excel + pureInfiniteScroll: Table Infinite Scroll + pureSensitive: Sensitive Filter + purePinyin: PinYin + pureDanmaku: Danmaku + pureSchemaForm: Form + pureTableBase: Base Usage + pureTableHigh: High Usage + pureTableEdit: Edit Usage + pureVxeTable: Virtual Usage + pureBoard: Paint Board + pureMindMap: Mind Map + pureMenuOverflow: Menu Overflow Show Tooltip Text + pureChildMenuOverflow: Child Menu Overflow Show Tooltip Text status: pureLoad: Loading... pureMessage: Message @@ -81,9 +193,42 @@ status: login: pureUsername: Username purePassword: Password + pureVerifyCode: VerifyCode + pureRemember: days no need to login + pureRememberInfo: After checking and logging in, will automatically log in to the system without entering your username and password within the specified number of days. + pureSure: Sure Password + pureForget: Forget Password? pureLogin: Login + pureThirdLogin: Third Login + purePhoneLogin: Phone Login + pureQRCodeLogin: QRCode Login + pureRegister: Register + pureWeChatLogin: WeChat Login + pureAlipayLogin: Alipay Login + pureQQLogin: QQ Login + pureWeiBoLogin: Weibo Login + purePhone: Phone + pureSmsVerifyCode: SMS VerifyCode + pureBack: Back + pureTest: Mock Test + pureTip: After scanning the code, click "Confirm" to complete the login + pureDefinite: Definite pureLoginSuccess: Login Success pureLoginFail: Login Fail + pureRegisterSuccess: Regist Success + pureTickPrivacy: Please tick Privacy Policy + pureReadAccept: I have read it carefully and accept + purePrivacyPolicy: Privacy Policy + pureGetVerifyCode: Get VerifyCode + pureInfo: Seconds pureUsernameReg: Please enter username purePassWordReg: Please enter password - purePassWordRuleReg: The password format should be any combination of 8-18 digits \ No newline at end of file + pureVerifyCodeReg: Please enter verify code + pureVerifyCodeCorrectReg: Please enter correct verify code + pureVerifyCodeSixReg: Please enter a 6-digit verify code + purePhoneReg: Please enter the phone + purePhoneCorrectReg: Please enter the correct phone number format + purePassWordRuleReg: The password format should be any combination of 8-18 digits + purePassWordSureReg: Please enter confirm password + purePassWordDifferentReg: The two passwords do not match! + purePassWordUpdateReg: Password has been updated \ No newline at end of file diff --git a/locales/vi.yaml b/locales/vi.yaml new file mode 100644 index 0000000..beffc4f --- /dev/null +++ b/locales/vi.yaml @@ -0,0 +1,238 @@ +buttons: + pureAccountSettings: Tài khoản + pureBackTop: Về đầu trang + pureClickCollapse: Thu gọn + pureClickExpand: Mở rộng + pureClose: Đóng + pureCloseAllTabs: Đóng tất cả các tab + pureCloseCurrentTab: Đóng tab hiện tại + pureCloseLeftTabs: Đóng các tab bên trái + pureCloseOtherTabs: Đóng các tab khác + pureCloseRightTabs: Đóng các tab bên phải + pureCloseText: Đóng + pureConfirm: Xác nhận + pureContentExitFullScreen: Thoát toàn màn hình nội dung + pureContentFullScreen: Toàn màn hình nội dung + pureLogin: Đăng nhập + pureLoginOut: Đăng xuất + pureOpenSystemSet: Mở Cấu hình Hệ thống + pureOpenText: Mở + pureReload: Tải lại + pureSwitch: Chuyển đổi +login: + pureAlipayLogin: Đăng nhập bằng Alipay + pureBack: Quay lại + pureDefinite: Xác định + pureForget: Quên mật khẩu? + pureGetVerifyCode: Lấy mã xác nhận + pureInfo: Giây + pureLogin: Đăng nhập + pureLoginFail: Đăng nhập thất bại + pureLoginSuccess: Đăng nhập thành công + purePassWordDifferentReg: Hai mật khẩu không khớp nhau! + purePassWordReg: Vui lòng nhập mật khẩu + purePassWordRuleReg: Định dạng mật khẩu phải là bất kỳ kết hợp nào từ 8-18 chữ số + purePassWordSureReg: Vui lòng nhập lại mật khẩu + purePassWordUpdateReg: Mật khẩu đã được cập nhật + purePassword: Mật khẩu + purePhone: Điện thoại + purePhoneCorrectReg: Vui lòng nhập đúng định dạng số điện thoại + purePhoneLogin: Đăng nhập bằng điện thoại + purePhoneReg: Vui lòng nhập số điện thoại + purePrivacyPolicy: Chính sách bảo mật + pureQQLogin: Đăng nhập bằng QQ + pureQRCodeLogin: Đăng nhập bằng mã QR + pureReadAccept: Tôi đã đọc và chấp nhận kỹ + pureRegister: Đăng ký + pureRegisterSuccess: Đăng ký thành công + pureRemember: ngày không cần đăng nhập + pureRememberInfo: >- + Sau khi chọn và đăng nhập, hệ thống sẽ tự động đăng nhập mà không cần nhập + lại tên người dùng và mật khẩu của bạn trong số ngày được chỉ định. + pureSmsVerifyCode: Mã xác nhận SMS + pureSure: Xác nhận mật khẩu + pureTest: Kiểm tra giả + pureThirdLogin: Đăng nhập bằng bên thứ ba + pureTickPrivacy: Vui lòng đánh dấu Chính sách bảo mật + pureTip: Sau khi quét mã, nhấn "Xác nhận" để hoàn thành đăng nhập + pureUsername: Tên người dùng + pureUsernameReg: Vui lòng nhập tên người dùng + pureVerifyCode: Mã xác nhận + pureVerifyCodeCorrectReg: Vui lòng nhập đúng mã xác nhận + pureVerifyCodeReg: Vui lòng nhập mã xác nhận + pureVerifyCodeSixReg: Vui lòng nhập mã xác nhận 6 chữ số + pureWeChatLogin: Đăng nhập bằng WeChat + pureWeiBoLogin: Đăng nhập bằng Weibo +menus: + pureAble: Able + pureAbnormal: Trang bất thường + pureAbout: Giới thiệu + pureAnimatecss: Lựa chọn AnimateCss + pureBarcode: Mã vạch + pureBoard: Bảng vẽ + pureButton: Hoạt hình nút + pureCardList: Trang danh sách thẻ + pureCascader: Cây địa phương + pureCheckButton: Nút kiểm tra + pureCheckCard: Thẻ kiểm tra + pureChildMenuOverflow: Hiển thị tooltip văn bản Menu con quá nhiều + pureCollapse: Thu gọn + pureColorHuntDoc: ColorHunt + pureColorPicker: Bảng chọn màu + pureComponents: Các thành phần + pureContextmenu: Menu ngữ cảnh + pureCountTo: Hiệu ứng số + pureCropping: Cắt ảnh + pureDanmaku: Danmaku + pureDatePicker: Chọn ngày + pureDateTimePicker: Chọn ngày giờ + pureDebounce: Debounce & Throttle + pureDept: Quản lý Bộ phận + pureDialog: Hộp thoại + pureDownload: Tải về + pureDraggable: Kéo thả + pureEditor: Biên tập viên + pureElButton: Nút + pureEmbeddedDoc: Tài liệu nhúng + pureEmpty: Trang rỗng + pureEpDoc: Element-Plus + pureExcel: Xuất Excel + pureExternalDoc: Tài liệu ngoài + pureExternalLink: Vue-Pure-Admin + pureExternalPage: Trang ngoài + pureFail: Trang thất bại + pureFive: '500' + pureFlowChart: Biểu đồ luồng + pureFormDesign: Thiết kế Form + pureFourZeroFour: '404' + pureFourZeroOne: '403' + pureGanttastic: Biểu đồ Gantt + pureGuide: Hướng dẫn + pureHome: Trang chủ + pureIconSelect: Lựa chọn biểu tượng + pureInfiniteScroll: Cuộn vô tận bảng + pureJsonEditor: Biên tập JSON + pureLineTree: Cây dòng + pureList: Trang danh sách + pureLogin: Đăng nhập + pureLoginLog: Log Đăng nhập + pureMap: Bản đồ + pureMenu1: Menu1 + pureMenu1-1: Menu1-1 + pureMenu1-2: Menu1-2 + pureMenu1-2-1: Menu1-2-1 + pureMenu1-2-2: Menu1-2-2 + pureMenu1-3: Menu1-3 + pureMenu2: Menu2 + pureMenuOverflow: Hiển thị tooltip văn bản Menu quá nhiều + pureMenuTree: Cây Menu + pureMenus: Menu đa cấp + pureMessage: Lời nhắn + pureMindMap: Mind Map + pureMqtt: Mqtt Client + pureOnlineUser: Người dùng trực tuyến + pureOperationLog: Log Hoạt động + pureOptimize: Debounce, Throttle, Copy, Longpress Directives + purePdf: Xem trước PDF + purePermission: Quản lý Phân quyền + purePermissionButton: Phân quyền Nút + purePermissionPage: Phân quyền Trang + purePiniaDoc: Pinia + purePinyin: PinYin + purePrint: In + pureProgress: Tiến độ + pureQrcode: Mã QR + pureResult: Trang kết quả + pureRipple: Ripple + pureRole: Quản lý Vai trò + pureRouterDoc: Vue-Router + pureSchemaForm: Form + pureSeamless: Cuộn liền mạch + pureSegmented: Chia nhỏ + pureSelector: Lựa chọn phạm vi + pureSensitive: Bộ lọc nhạy cảm + pureSplitPane: Split Pane + pureStatistic: Thống kê + pureSuccess: Trang thành công + pureSwiper: Plugin Swiper + pureSysManagement: Quản lý Hệ thống + pureSysMonitor: Giám sát Hệ thống + pureSystemLog: Log Hệ thống + pureSystemMenu: Quản lý Menu + pureTable: Bảng + pureTableBase: Sử dụng cơ bản bảng + pureTableEdit: Sử dụng chỉnh sửa bảng + pureTableHigh: Sử dụng cao bảng + pureTabs: Quản lý Tab + pureTag: Thẻ + pureTailwindcssDoc: Tailwindcss + pureText: Chữ ellipsis + pureTimePicker: Chọn giờ + pureTimeline: Dòng thời gian + pureTypeit: Typeit + pureUiGradients: UiGradients + pureUpload: Tải lên tệp + pureUser: Quản lý Người dùng + pureUtilsLink: Pure-Admin-Utils + pureVerify: Mã xác nhận + pureVideo: Video + pureVideoFrame: Chụp Khung Video + pureVirtualList: Danh sách ảo + pureViteDoc: Vite + pureVueDoc: Vue3 + pureVxeTable: Sử dụng ảo hóa bảng + pureWaterfall: Thác nước + pureWatermark: Water Mark + pureWavesurfer: Biểu đồ trực quan âm thanh +panel: + pureClearCache: Xóa Cache + pureClearCacheAndToLogin: Xóa bộ nhớ cache và quay lại trang đăng nhập + pureCloseSystemSet: Đóng Cấu hình Hệ thống + pureGreyModel: Giao diện xám + pureHiddenFooter: Ẩn footer + pureHiddenTags: Ẩn tab + pureHorizontalTip: Menu trên cùng, tổng quan súc tích + pureInterfaceDisplay: Hiển thị giao diện + pureLayoutModel: Giao diện bố cục + pureMixTip: Menu pha trộn, linh hoạt + pureMultiTagsCache: MultiTags Cache + pureOverallStyle: Kiểu tổng thể + pureOverallStyleDark: Tối + pureOverallStyleDarkTip: Làm việc dưới ánh trăng, thưởng thức sự thanh tao và tĩnh lặng của đêm + pureOverallStyleLight: Sáng + pureOverallStyleLightTip: Bắt đầu mới và sáng tạo giao diện làm việc thoải mái + pureOverallStyleSystem: Tự động + pureOverallStyleSystemTip: >- + Đồng bộ hóa thời gian, giao diện tự nhiên phản ứng với buổi sáng và hoàng + hôn + pureStretch: Kéo dài trang + pureStretchCustom: Tùy chỉnh + pureStretchCustomTip: Tối thiểu 1280, tối đa 1600 + pureStretchFixed: Cố định + pureStretchFixedTip: Trang nhỏ gọn giúp dễ dàng tìm thông tin bạn cần + pureSystemSet: Cấu hình Hệ thống + pureTagsStyle: Kiểu tab + pureTagsStyleCard: Tab + pureTagsStyleCardTip: Tab cho việc duyệt web hiệu quả + pureTagsStyleChrome: Chrome + pureTagsStyleChromeTip: Kiểu Chrome mang phong cách cổ điển và thanh lịch + pureTagsStyleSmart: Thông minh + pureTagsStyleSmartTip: Thẻ thông minh thêm sự sáng tạo và lấp lánh + pureThemeColor: Màu chủ đề + pureVerticalTip: Menu bên trái quen thuộc và thân thiện + pureWeakModel: Mô hình yếu +search: + pureCollect: Thu gom + pureDragSort: Sắp xếp kéo thả + pureEmpty: Trống + pureHistory: Lịch sử + purePlaceholder: Tìm kiếm Menu + pureTotal: Tổng số +status: + pureLoad: Đang tải... + pureMessage: Thông báo + pureNoMessage: Không có thông báo + pureNoNotify: Không có thông báo + pureNoTodo: Không có công việc cần làm + pureNotify: Thông báo + pureTodo: Công việc cần làm diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml deleted file mode 100644 index 2a41d12..0000000 --- a/locales/zh-CN.yaml +++ /dev/null @@ -1,89 +0,0 @@ -buttons: - pureLoginOut: 退出系统 - pureLogin: 登录 - pureOpenSystemSet: 打开系统配置 - pureReload: 重新加载 - pureCloseCurrentTab: 关闭当前标签页 - pureCloseLeftTabs: 关闭左侧标签页 - pureCloseRightTabs: 关闭右侧标签页 - pureCloseOtherTabs: 关闭其他标签页 - pureCloseAllTabs: 关闭全部标签页 - pureContentFullScreen: 内容区全屏 - pureContentExitFullScreen: 内容区退出全屏 - pureClickCollapse: 点击折叠 - pureClickExpand: 点击展开 - pureConfirm: 确认 - pureSwitch: 切换 - pureClose: 关闭 - pureBackTop: 回到顶部 - pureOpenText: 开 - pureCloseText: 关 -search: - pureTotal: 共 - pureHistory: 搜索历史 - pureCollect: 收藏 - pureDragSort: (可拖拽排序) - pureEmpty: 暂无搜索结果 - purePlaceholder: 搜索菜单(支持拼音搜索) -panel: - pureSystemSet: 系统配置 - pureCloseSystemSet: 关闭配置 - pureClearCacheAndToLogin: 清空缓存并返回登录页 - pureClearCache: 清空缓存 - pureOverallStyle: 整体风格 - pureOverallStyleLight: 浅色 - pureOverallStyleLightTip: 清新启航,点亮舒适的工作界面 - pureOverallStyleDark: 深色 - pureOverallStyleDarkTip: 月光序曲,沉醉于夜的静谧雅致 - pureOverallStyleSystem: 自动 - pureOverallStyleSystemTip: 同步时光,界面随晨昏自然呼应 - pureThemeColor: 主题色 - pureLayoutModel: 导航模式 - pureVerticalTip: 左侧菜单,亲切熟悉 - pureHorizontalTip: 顶部菜单,简洁概览 - pureMixTip: 混合菜单,灵活多变 - pureStretch: 页宽 - pureStretchFixed: 固定 - pureStretchFixedTip: 紧凑页面,轻松找到所需信息 - pureStretchCustom: 自定义 - pureStretchCustomTip: 最小1280、最大1600 - pureTagsStyle: 页签风格 - pureTagsStyleSmart: 灵动 - pureTagsStyleSmartTip: 灵动标签,添趣生辉 - pureTagsStyleCard: 卡片 - pureTagsStyleCardTip: 卡片标签,高效浏览 - pureTagsStyleChrome: 谷歌 - pureTagsStyleChromeTip: 谷歌风格,经典美观 - pureInterfaceDisplay: 界面显示 - pureGreyModel: 灰色模式 - pureWeakModel: 色弱模式 - pureHiddenTags: 隐藏标签页 - pureHiddenFooter: 隐藏页脚 - pureMultiTagsCache: 页签持久化 -menus: - pureHome: 首页 - pureLogin: 登录 - pureAbnormal: 异常页面 - pureFourZeroFour: "404" - pureFourZeroOne: "403" - pureFive: "500" - purePermission: 权限管理 - purePermissionPage: 页面权限 - purePermissionButton: 按钮权限 -status: - pureLoad: 加载中... - pureMessage: 消息 - pureNotify: 通知 - pureTodo: 待办 - pureNoMessage: 暂无消息 - pureNoNotify: 暂无通知 - pureNoTodo: 暂无待办 -login: - pureUsername: 账号 - purePassword: 密码 - pureLogin: 登录 - pureLoginSuccess: 登录成功 - pureLoginFail: 登录失败 - pureUsernameReg: 请输入账号 - purePassWordReg: 请输入密码 - purePassWordRuleReg: 密码格式应为8-18位数字、字母、符号的任意两种组合 \ No newline at end of file diff --git a/mock/asyncRoutes.ts b/mock/asyncRoutes.ts index fef84ef..effd3e4 100644 --- a/mock/asyncRoutes.ts +++ b/mock/asyncRoutes.ts @@ -45,6 +45,8 @@ export default defineFakeRoute([ response: () => { return { success: true, + msg: "success", + msgDev: null, data: [permissionRouter] }; } diff --git a/package.json b/package.json index f60be3c..cc3de9e 100644 --- a/package.json +++ b/package.json @@ -50,8 +50,13 @@ "@pureadmin/descriptions": "^1.2.1", "@pureadmin/table": "^3.1.2", "@pureadmin/utils": "^2.4.7", + "@vue-flow/background": "^1.3.0", + "@vue-flow/core": "^1.38.2", "@vueuse/core": "^10.11.0", "@vueuse/motion": "^2.2.3", + "@wangeditor/editor": "^5.1.23", + "@wangeditor/editor-for-vue": "^5.1.12", + "@zxcvbn-ts/core": "^3.0.4", "animate.css": "^4.1.1", "axios": "^1.7.2", "dayjs": "^1.11.11", @@ -70,8 +75,15 @@ "vue": "^3.4.31", "vue-i18n": "^9.13.1", "vue-router": "^4.4.0", + "xlsx": "^0.18.5", "vue-tippy": "^6.4.4", - "vue-types": "^5.1.2" + "vue-types": "^5.1.2", + "vue-virtual-scroller": "2.0.0-beta.8", + "vue-waterfall-plugin-next": "^2.4.3", + "vue3-danmaku": "^1.6.0", + "vue3-puzzle-vcode": "^1.1.7", + "vuedraggable": "^4.1.0", + "vxe-table": "4.6.17" }, "devDependencies": { "@commitlint/cli": "^19.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fd39a94..bd82a3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,12 +17,27 @@ importers: '@pureadmin/utils': specifier: ^2.4.7 version: 2.4.7(echarts@5.5.1)(vue@3.4.31(typescript@5.5.3)) + '@vue-flow/background': + specifier: ^1.3.0 + version: 1.3.0(@vue-flow/core@1.38.5(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3)) + '@vue-flow/core': + specifier: ^1.38.2 + version: 1.38.5(vue@3.4.31(typescript@5.5.3)) '@vueuse/core': specifier: ^10.11.0 version: 10.11.0(vue@3.4.31(typescript@5.5.3)) '@vueuse/motion': specifier: ^2.2.3 version: 2.2.3(rollup@4.18.0)(vue@3.4.31(typescript@5.5.3)) + '@wangeditor/editor': + specifier: ^5.1.23 + version: 5.1.23 + '@wangeditor/editor-for-vue': + specifier: ^5.1.12 + version: 5.1.12(@wangeditor/editor@5.1.23)(vue@3.4.31(typescript@5.5.3)) + '@zxcvbn-ts/core': + specifier: ^3.0.4 + version: 3.0.4 animate.css: specifier: ^4.1.1 version: 4.1.1 @@ -83,6 +98,27 @@ importers: vue-types: specifier: ^5.1.2 version: 5.1.2(vue@3.4.31(typescript@5.5.3)) + vue-virtual-scroller: + specifier: 2.0.0-beta.8 + version: 2.0.0-beta.8(vue@3.4.31(typescript@5.5.3)) + vue-waterfall-plugin-next: + specifier: ^2.4.3 + version: 2.4.3(@types/lodash-es@4.17.12)(vue@3.4.31(typescript@5.5.3)) + vue3-danmaku: + specifier: ^1.6.0 + version: 1.6.0(vue@3.4.31(typescript@5.5.3)) + vue3-puzzle-vcode: + specifier: ^1.1.7 + version: 1.1.7 + vuedraggable: + specifier: ^4.1.0 + version: 4.1.0(vue@3.4.31(typescript@5.5.3)) + vxe-table: + specifier: 4.6.17 + version: 4.6.17(vue@3.4.31(typescript@5.5.3)) + xlsx: + specifier: ^0.18.5 + version: 0.18.5 devDependencies: '@commitlint/cli': specifier: ^19.3.0 @@ -418,6 +454,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.24.7': + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} + engines: {node: '>=6.9.0'} + '@babel/standalone@7.24.7': resolution: {integrity: sha512-QRIRMJ2KTeN+vt4l9OjYlxDVXEpcor1Z6V7OeYzeBOw6Q8ew9oMTHjzTx8s6ClsZO7wVf6JgTRutihatN6K0yA==} engines: {node: '>=6.9.0'} @@ -533,6 +573,11 @@ packages: '@dual-bundle/import-meta-resolve@4.1.0': resolution: {integrity: sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==} + '@element-plus/icons-vue@1.1.4': + resolution: {integrity: sha512-Iz/nHqdp1sFPmdzRwHkEQQA3lKvoObk8azgABZ81QUOpW9s/lUyQVUSh0tNtEPZXQlKwlSh7SPgoVxzrE0uuVQ==} + peerDependencies: + vue: ^3.2.0 + '@element-plus/icons-vue@2.3.1': resolution: {integrity: sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==} peerDependencies: @@ -1024,55 +1069,46 @@ packages: resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.18.0': resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.18.0': resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.18.0': resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.18.0': resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-s390x-gnu@4.18.0': resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.18.0': resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.18.0': resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.18.0': resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} @@ -1096,6 +1132,9 @@ packages: '@sxzz/popperjs-es@2.11.7': resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==} + '@transloadit/prettier-bytes@0.0.7': + resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==} + '@trysound/sax@0.2.0': resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} engines: {node: '>=10.13.0'} @@ -1106,6 +1145,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/event-emitter@0.3.5': + resolution: {integrity: sha512-zx2/Gg0Eg7gwEiOIIh5w9TrhKKTeQh7CPCOPNc0el4pLSwzebA8SmnHwZs2dWlLONvyulykSwGSQxQHLhjGLvQ==} + '@types/gradient-string@1.1.6': resolution: {integrity: sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ==} @@ -1133,6 +1175,9 @@ packages: '@types/tinycolor2@1.4.6': resolution: {integrity: sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==} + '@types/web-bluetooth@0.0.14': + resolution: {integrity: sha512-5d2RhCard1nQUC3aHcq/gHzWYO6K0WJmAbjO7mQJgCQKtZpgXxv1rOM6O/dBDhDYYVutk1sciOgNSe+5YyfM8A==} + '@types/web-bluetooth@0.0.16': resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==} @@ -1197,6 +1242,23 @@ packages: resolution: {integrity: sha512-Hqgy/ETgpt2L5xueA/zHHIl4fJI2O4XUE9l4+OIfbJIRSnTJb/QscncdqqZzofQegIJugRIF57OJea1khw2SDw==} engines: {node: ^18.18.0 || >=20.0.0} + '@uppy/companion-client@2.2.2': + resolution: {integrity: sha512-5mTp2iq97/mYSisMaBtFRry6PTgZA6SIL7LePteOV5x0/DxKfrZW3DEiQERJmYpHzy7k8johpm2gHnEKto56Og==} + + '@uppy/core@2.3.4': + resolution: {integrity: sha512-iWAqppC8FD8mMVqewavCz+TNaet6HPXitmGXpGGREGrakZ4FeuWytVdrelydzTdXx6vVKkOmI2FLztGg73sENQ==} + + '@uppy/store-default@2.1.1': + resolution: {integrity: sha512-xnpTxvot2SeAwGwbvmJ899ASk5tYXhmZzD/aCFsXePh/v8rNvR2pKlcQUH7cF/y4baUGq3FHO/daKCok/mpKqQ==} + + '@uppy/utils@4.1.3': + resolution: {integrity: sha512-nTuMvwWYobnJcytDO3t+D6IkVq/Qs4Xv3vyoEZ+Iaf8gegZP+rEyoaFT2CK5XLRMienPyqRqNbIfRuFaOWSIFw==} + + '@uppy/xhr-upload@2.1.3': + resolution: {integrity: sha512-YWOQ6myBVPs+mhNjfdWsQyMRWUlrDLMoaG7nvf/G6Y3GKZf8AyjFDjvvJ49XWQ+DaZOftGkHmF1uh/DBeGivJQ==} + peerDependencies: + '@uppy/core': ^2.3.3 + '@vitejs/plugin-vue-jsx@4.0.0': resolution: {integrity: sha512-A+6wL2AdQhDsLsDnY+2v4rRDI1HLJGIMc97a8FURO9tqKsH5QvjWrzsa5DH3NlZsM742W2wODl2fF+bfcTWtXw==} engines: {node: ^18.0.0 || >=20.0.0} @@ -1229,6 +1291,17 @@ packages: '@volar/typescript@2.4.0-alpha.12': resolution: {integrity: sha512-mLg+OQauMTv/+08a7WBWJo1sev/wc8t2is0zhBZIlFU+j5mG89FM4+4089c2p/zoUFZ400Q/VNg2BPfhpZ8wSA==} + '@vue-flow/background@1.3.0': + resolution: {integrity: sha512-fu/8s9wzSOQIitnSTI10XT3bzTtagh4h8EF2SWwtlDklOZjAaKy75lqv4htHa3wigy/r4LGCOGwLw3Pk88/AxA==} + peerDependencies: + '@vue-flow/core': ^1.23.0 + vue: ^3.3.0 + + '@vue-flow/core@1.38.5': + resolution: {integrity: sha512-Bu1bmHNeyDN1r26yNCfrM2zJGS8S5w/H8yJJf2AtIALQT6TwOuOn8y0urzwbX8LqLW2h896O89yIOSkVC2lEOA==} + peerDependencies: + vue: ^3.3.0 + '@vue/babel-helper-vue-transform-on@1.2.2': resolution: {integrity: sha512-nOttamHUR3YzdEqdM/XXDyCSdxMA9VizUKoroLX6yTyRtggzQMHXcmwh8a7ZErcJttIBIc9s68a1B8GZ+Dmvsw==} @@ -1288,12 +1361,26 @@ packages: '@vueuse/core@10.11.0': resolution: {integrity: sha512-x3sD4Mkm7PJ+pcq3HX8PLPBadXCAlSDR/waK87dz0gQE+qJnaaFhc/dZVfJz+IUYzTMVGum2QlR7ImiJQN4s6g==} + '@vueuse/core@8.9.4': + resolution: {integrity: sha512-B/Mdj9TK1peFyWaPof+Zf/mP9XuGAngaJZBwPaXBvU3aCTZlx3ltlrFFFyMV4iGBwsjSCeUCgZrtkEj9dS2Y3Q==} + peerDependencies: + '@vue/composition-api': ^1.1.0 + vue: ^2.6.0 || ^3.2.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue: + optional: true + '@vueuse/core@9.13.0': resolution: {integrity: sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==} '@vueuse/metadata@10.11.0': resolution: {integrity: sha512-kQX7l6l8dVWNqlqyN3ePW3KmjCQO3ZMgXuBMddIu83CmucrsBfXlH+JoviYyRBws/yLTQO8g3Pbw+bdIoVm4oQ==} + '@vueuse/metadata@8.9.4': + resolution: {integrity: sha512-IwSfzH80bnJMzqhaapqJl9JRIiyQU0zsRGEgnxN6jhq7992cPUJIRfV+JHRIZXjYqbwt07E1gTEp0R0zPJ1aqw==} + '@vueuse/metadata@9.13.0': resolution: {integrity: sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==} @@ -1305,14 +1392,115 @@ packages: '@vueuse/shared@10.11.0': resolution: {integrity: sha512-fyNoIXEq3PfX1L3NkNhtVQUSRtqYwJtJg+Bp9rIzculIZWHTkKSysujrOk2J+NrRulLTQH9+3gGSfYLWSEWU1A==} + '@vueuse/shared@8.9.4': + resolution: {integrity: sha512-wt+T30c4K6dGRMVqPddexEVLa28YwxW5OFIPmzUHICjphfAuBFTTdDoyqREZNDOFJZ44ARH1WWQNCUK8koJ+Ag==} + peerDependencies: + '@vue/composition-api': ^1.1.0 + vue: ^2.6.0 || ^3.2.0 + peerDependenciesMeta: + '@vue/composition-api': + optional: true + vue: + optional: true + '@vueuse/shared@9.13.0': resolution: {integrity: sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==} + '@wangeditor/basic-modules@1.1.7': + resolution: {integrity: sha512-cY9CPkLJaqF05STqfpZKWG4LpxTMeGSIIF1fHvfm/mz+JXatCagjdkbxdikOuKYlxDdeqvOeBmsUBItufDLXZg==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/code-highlight@1.0.3': + resolution: {integrity: sha512-iazHwO14XpCuIWJNTQTikqUhGKyqj+dUNWJ9288Oym9M2xMVHvnsOmDU2sgUDWVy+pOLojReMPgXCsvvNlOOhw==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/core@1.1.19': + resolution: {integrity: sha512-KevkB47+7GhVszyYF2pKGKtCSj/YzmClsD03C3zTt+9SR2XWT5T0e3yQqg8baZpcMvkjs1D8Dv4fk8ok/UaS2Q==} + peerDependencies: + '@uppy/core': ^2.1.1 + '@uppy/xhr-upload': ^2.0.3 + dom7: ^3.0.0 + is-hotkey: ^0.2.0 + lodash.camelcase: ^4.3.0 + lodash.clonedeep: ^4.5.0 + lodash.debounce: ^4.0.8 + lodash.foreach: ^4.5.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + lodash.toarray: ^4.4.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/editor-for-vue@5.1.12': + resolution: {integrity: sha512-0Ds3D8I+xnpNWezAeO7HmPRgTfUxHLMd9JKcIw+QzvSmhC5xUHbpCcLU+KLmeBKTR/zffnS5GQo6qi3GhTMJWQ==} + peerDependencies: + '@wangeditor/editor': '>=5.1.0' + vue: ^3.0.5 + + '@wangeditor/editor@5.1.23': + resolution: {integrity: sha512-0RxfeVTuK1tktUaPROnCoFfaHVJpRAIE2zdS0mpP+vq1axVQpLjM8+fCvKzqYIkH0Pg+C+44hJpe3VVroSkEuQ==} + + '@wangeditor/list-module@1.0.5': + resolution: {integrity: sha512-uDuYTP6DVhcYf7mF1pTlmNn5jOb4QtcVhYwSSAkyg09zqxI1qBqsfUnveeDeDqIuptSJhkh81cyxi+MF8sEPOQ==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/table-module@1.1.4': + resolution: {integrity: sha512-5saanU9xuEocxaemGdNi9t8MCDSucnykEC6jtuiT72kt+/Hhh4nERYx1J20OPsTCCdVr7hIyQenFD1iSRkIQ6w==} + peerDependencies: + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.isequal: ^4.5.0 + lodash.throttle: ^4.1.1 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/upload-image-module@1.0.2': + resolution: {integrity: sha512-z81lk/v71OwPDYeQDxj6cVr81aDP90aFuywb8nPD6eQeECtOymrqRODjpO6VGvCVxVck8nUxBHtbxKtjgcwyiA==} + peerDependencies: + '@uppy/core': ^2.0.3 + '@uppy/xhr-upload': ^2.0.3 + '@wangeditor/basic-modules': 1.x + '@wangeditor/core': 1.x + dom7: ^3.0.0 + lodash.foreach: ^4.5.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + + '@wangeditor/video-module@1.1.4': + resolution: {integrity: sha512-ZdodDPqKQrgx3IwWu4ZiQmXI8EXZ3hm2/fM6E3t5dB8tCaIGWQZhmqd6P5knfkRAd3z2+YRSRbxOGfoRSp/rLg==} + peerDependencies: + '@uppy/core': ^2.1.4 + '@uppy/xhr-upload': ^2.0.7 + '@wangeditor/core': 1.x + dom7: ^3.0.0 + nanoid: ^3.2.0 + slate: ^0.72.0 + snabbdom: ^3.1.0 + '@zougt/some-loader-utils@1.4.3': resolution: {integrity: sha512-0FsoqSTQ+qOyp6x5Q6LZQ7xVwquEgLYiIStG3L8p0Q2GsGGYKDkOZ0mIpMt67aNdr8XLsbxXjzTl/iHtTz5zcA==} engines: {node: '>= 10.13.0'} hasBin: true + '@zxcvbn-ts/core@3.0.4': + resolution: {integrity: sha512-aQeiT0F09FuJaAqNrxynlAwZ2mW/1MdXakKWNmGM1Qp/VaY6CnB/GfnMS2T8gB2231Esp1/maCWd8vTG4OuShw==} + JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -1327,6 +1515,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1480,6 +1672,10 @@ packages: caniuse-lite@1.0.30001639: resolution: {integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==} + cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} @@ -1519,6 +1715,10 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -1571,6 +1771,9 @@ packages: compatx@0.1.8: resolution: {integrity: sha512-jcbsEAR81Bt5s1qOFymBufmCbXCXbk0Ql+K5ouj6gCyx2yHlu6AgmGIi9HxfKixpUDO5bCFJUHQ5uM6ecbTebw==} + compute-scroll-into-view@1.0.20: + resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==} + computeds@0.0.1: resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==} @@ -1617,6 +1820,11 @@ packages: typescript: optional: true + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -1717,6 +1925,48 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d@1.0.2: + resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==} + engines: {node: '>=0.12'} + dargs@8.1.0: resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} engines: {node: '>=12'} @@ -1773,6 +2023,12 @@ packages: dom-serializer@2.0.0: resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + dom-zindex@1.0.4: + resolution: {integrity: sha512-PNk7u71TJ1C9Lwjjp5nNuQcVWuECFMmr9kZAwi2UbgWUM7jXdTCe4O4x5bhLUa07jpcZUVA5Du3ho7/FXzS9Ng==} + + dom7@3.0.0: + resolution: {integrity: sha512-oNlcUdHsC4zb7Msx7JN3K0Nro1dzJ48knvBOnDPKJ2GV9wl1i5vydJZUSyOfrkKFDZEud/jBsTk92S/VGSAe/g==} + domelementtype@2.3.0: resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} @@ -1807,6 +2063,11 @@ packages: electron-to-chromium@1.4.816: resolution: {integrity: sha512-EKH5X5oqC6hLmiS7/vYtZHZFTNdhsYG5NVPRN6Yn0kQHNBlT59+xSM8HBy66P5fxWpKgZbPqb+diC64ng295Jw==} + element-plus@2.1.4: + resolution: {integrity: sha512-pcwgDbKUrzyWbixYB/zIbLPLBQ/NPGPJnGXJ+jYozUSthPW4SuriaUGJKgbAE6PDBAtw3IodiT2E2GbiaZLxww==} + peerDependencies: + vue: ^3.2.0 + element-plus@2.7.6: resolution: {integrity: sha512-36sw1K23hYjgeooR10U6CiCaCp2wvOqwoFurADZVlekeQ9v5U1FhJCFGEXO6i/kZBBMwsE1c9fxjLs9LENw2Rg==} peerDependencies: @@ -1846,6 +2107,17 @@ packages: es-module-lexer@0.4.1: resolution: {integrity: sha512-ooYciCUtfw6/d2w56UVeqHPcoCFAiJdz5XOkYpv/Txl1HMUozpXjz/2RIQgqwKdXNDPSF1W7mJCFse3G+HDyAA==} + es5-ext@0.10.64: + resolution: {integrity: sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==} + engines: {node: '>=0.10'} + + es6-iterator@2.0.3: + resolution: {integrity: sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==} + + es6-symbol@3.1.4: + resolution: {integrity: sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==} + engines: {node: '>=0.12'} + esbuild@0.19.12: resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==} engines: {node: '>=12'} @@ -1931,6 +2203,10 @@ packages: engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} hasBin: true + esniff@2.0.1: + resolution: {integrity: sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==} + engines: {node: '>=0.10'} + espree@10.1.0: resolution: {integrity: sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -1966,6 +2242,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + event-emitter@0.3.5: + resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==} + eventemitter3@5.0.1: resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} @@ -1973,6 +2252,9 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + ext@1.7.0: + resolution: {integrity: sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -2044,6 +2326,10 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -2203,6 +2489,9 @@ packages: resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} engines: {node: '>=8'} + html-void-elements@2.0.1: + resolution: {integrity: sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==} + htmlparser2@8.0.2: resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} @@ -2210,6 +2499,9 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + i18next@20.6.1: + resolution: {integrity: sha512-yCMYTMEJ9ihCwEQQ3phLo7I/Pwycf8uAx+sRHwwk5U9Aui/IZYgQRyMqXafQOw5QQ7DM1Z+WyEXWIqSuJHhG2A==} + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} @@ -2217,6 +2509,9 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + immer@9.0.21: + resolution: {integrity: sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==} + immutable@4.3.6: resolution: {integrity: sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==} @@ -2286,6 +2581,9 @@ packages: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} + is-hotkey@0.2.0: + resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==} + is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2313,6 +2611,9 @@ packages: resolution: {integrity: sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==} engines: {node: '>=8'} + is-url@1.2.4: + resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} + is-wsl@2.2.0: resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} engines: {node: '>=8'} @@ -2453,6 +2754,18 @@ packages: lodash.camelcase@4.3.0: resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.foreach@4.5.0: + resolution: {integrity: sha512-aEXTF4d+m05rVOAUG3z4vZZ4xVexLKZGF0lIxuHZ1Hplpk/3B6Z1+/ICICYRLm7c41Z2xiejbkCkJoTlypoXhQ==} + + lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} @@ -2474,6 +2787,12 @@ packages: lodash.startcase@4.4.0: resolution: {integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==} + lodash.throttle@4.1.1: + resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==} + + lodash.toarray@4.4.0: + resolution: {integrity: sha512-QyffEA3i5dma5q2490+SgCvDN0pXLmRGSyAANuVi0HQ01Pkfr9fuoKQW8wm1wGBnJITs/mS7wQvS6VshUEBFCw==} + lodash.truncate@4.4.2: resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} @@ -2541,6 +2860,9 @@ packages: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} + mime-match@1.0.2: + resolution: {integrity: sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==} + mime-types@2.1.35: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} @@ -2579,6 +2901,9 @@ packages: resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} engines: {node: '>= 8'} + mitt@2.1.0: + resolution: {integrity: sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg==} + mitt@3.0.1: resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} @@ -2603,6 +2928,9 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + namespace-emitter@2.0.1: + resolution: {integrity: sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g==} + nanoid@3.3.7: resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -2611,6 +2939,9 @@ packages: natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + next-tick@1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + node-fetch-native@1.6.4: resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} @@ -3199,6 +3530,9 @@ packages: resolution: {integrity: sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==} engines: {node: ^10 || ^12 || >=14} + preact@10.22.1: + resolution: {integrity: sha512-jRYbDDgMpIb5LHq3hkI0bbl+l/TQ9UnkdQ0ww+lp+4MMOdqaUYdFc5qeyP+IV8FAd/2Em7drVPeKdQxsiWCf/A==} + prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} @@ -3217,6 +3551,10 @@ packages: engines: {node: '>=14'} hasBin: true + prismjs@1.29.0: + resolution: {integrity: sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==} + engines: {node: '>=6'} + process@0.11.10: resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} engines: {node: '>= 0.6.0'} @@ -3245,6 +3583,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -3315,6 +3656,9 @@ packages: engines: {node: '>=14.0.0'} hasBin: true + scroll-into-view-if-needed@2.2.31: + resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==} + scule@1.3.0: resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} @@ -3361,6 +3705,14 @@ packages: resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} engines: {node: '>=14.16'} + slate-history@0.66.0: + resolution: {integrity: sha512-6MWpxGQZiMvSINlCbMW43E2YBSVMCMCIwQfBzGssjWw4kb0qfvj0pIdblWNRQZD0hR6WHP+dHHgGSeVdMWzfng==} + peerDependencies: + slate: '>=0.65.3' + + slate@0.72.8: + resolution: {integrity: sha512-/nJwTswQgnRurpK+bGJFH1oM7naD5qDmHd89JyiKNT2oOKD8marW0QSBtuFnwEbL5aGCS8AmrhXQgNOsn4osAw==} + slice-ansi@4.0.0: resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} engines: {node: '>=10'} @@ -3373,6 +3725,13 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} + snabbdom@3.6.2: + resolution: {integrity: sha512-ig5qOnCDbugFntKi6c7Xlib8bA6xiJVk8O+WdFrV3wxbMqeHO0hXFQC4nAhPVWfZfi8255lcZkNhtIBINCc4+Q==} + engines: {node: '>=12.17.0'} + + sortablejs@1.14.0: + resolution: {integrity: sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==} + sortablejs@1.15.2: resolution: {integrity: sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==} @@ -3396,6 +3755,13 @@ packages: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + + ssr-window@3.0.0: + resolution: {integrity: sha512-q+8UfWDg9Itrg0yWK7oe5p/XRCJpJF9OBtXfOPgSJl+u3Xd5KI328RUEvUqSMVM9CiQUEf1QdBzJMkYGErj9QA==} + stable@0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' @@ -3601,6 +3967,9 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-warning@1.0.3: + resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==} + tinycolor2@1.6.0: resolution: {integrity: sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==} @@ -3652,6 +4021,9 @@ packages: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} + type@2.7.3: + resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==} + typescript@5.5.3: resolution: {integrity: sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==} engines: {node: '>=14.17'} @@ -3846,6 +4218,16 @@ packages: peerDependencies: vue: ^3.0.0 + vue-observe-visibility@2.0.0-alpha.1: + resolution: {integrity: sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==} + peerDependencies: + vue: ^3.0.0 + + vue-resize@2.0.0-alpha.1: + resolution: {integrity: sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==} + peerDependencies: + vue: ^3.0.0 + vue-router@4.4.0: resolution: {integrity: sha512-HB+t2p611aIZraV2aPSRNXf0Z/oLZFrlygJm+sZbdJaW6lcFqEDQwnzUBXn+DApw+/QzDU/I9TeWx9izEjTmsA==} peerDependencies: @@ -3874,6 +4256,22 @@ packages: vue: optional: true + vue-virtual-scroller@2.0.0-beta.8: + resolution: {integrity: sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==} + peerDependencies: + vue: ^3.2.0 + + vue-waterfall-plugin-next@2.4.3: + resolution: {integrity: sha512-L2mH8wOn+ZuddPhaYHKljfukNM0iQd3vhjesvBK4ck+D5swS8c34VdlOED2w53QZvUrXQpEg9ASDHzLlLkzztg==} + + vue3-danmaku@1.6.0: + resolution: {integrity: sha512-XjwVKIelupDD3PWn6k22l5qS8y+SCdFUYq4sSpcPInqk7CyzXWSAfz2BL6WWx9HU9CRWS3x2oDMkepLkJoWvNQ==} + peerDependencies: + vue: ^3.0.0 + + vue3-puzzle-vcode@1.1.7: + resolution: {integrity: sha512-mW780dz7HKjrElnE60CeYSeHGidKBKHoMjTDYfqF21330rTkFOsfDK1FQKZ22MktgMtTEoS/imfpEDlM1cxY/g==} + vue@3.4.31: resolution: {integrity: sha512-njqRrOy7W3YLAlVqSKpBebtZpDVg21FPoaq1I7f/+qqBThK9ChAIjkRWgeP6Eat+8C+iia4P3OYqpATP21BCoQ==} peerDependencies: @@ -3882,6 +4280,16 @@ packages: typescript: optional: true + vuedraggable@4.1.0: + resolution: {integrity: sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==} + peerDependencies: + vue: ^3.0.1 + + vxe-table@4.6.17: + resolution: {integrity: sha512-K9A0qJuV33Xh2NK6L1tXXEXueGyOoRVz7eFvFFpTbLVtm3oCoYl6+BOqD6YTw70R8NKdZZNH+ylmEJug+Qjrrg==} + peerDependencies: + vue: ^3.2.28 + webpack-sources@3.2.3: resolution: {integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==} engines: {node: '>=10.13.0'} @@ -3902,10 +4310,21 @@ packages: resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==} engines: {node: '>=12'} + wildcard@1.1.2: + resolution: {integrity: sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng==} + + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3922,6 +4341,14 @@ packages: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + xe-utils@3.5.28: + resolution: {integrity: sha512-oeLLJ0b54QdOSSgYQ9TiKW/xAGrc9r0weCA/5UfyGdm3n3js4cNOuuf9Tml7UwgBQpl4uWMbMwUZKLh2yqPF3A==} + + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -4179,6 +4606,10 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/runtime@7.24.7': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/standalone@7.24.7': optional: true @@ -4339,6 +4770,10 @@ snapshots: '@dual-bundle/import-meta-resolve@4.1.0': {} + '@element-plus/icons-vue@1.1.4(vue@3.4.31(typescript@5.5.3))': + dependencies: + vue: 3.4.31(typescript@5.5.3) + '@element-plus/icons-vue@2.3.1(vue@3.4.31(typescript@5.5.3))': dependencies: vue: 3.4.31(typescript@5.5.3) @@ -4773,6 +5208,8 @@ snapshots: '@sxzz/popperjs-es@2.11.7': {} + '@transloadit/prettier-bytes@0.0.7': {} + '@trysound/sax@0.2.0': {} '@types/conventional-commits-parser@5.0.0': @@ -4781,6 +5218,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/event-emitter@0.3.5': {} + '@types/gradient-string@1.1.6': dependencies: '@types/tinycolor2': 1.4.6 @@ -4805,6 +5244,8 @@ snapshots: '@types/tinycolor2@1.4.6': {} + '@types/web-bluetooth@0.0.14': {} + '@types/web-bluetooth@0.0.16': {} '@types/web-bluetooth@0.0.20': {} @@ -4890,6 +5331,35 @@ snapshots: '@typescript-eslint/types': 7.15.0 eslint-visitor-keys: 3.4.3 + '@uppy/companion-client@2.2.2': + dependencies: + '@uppy/utils': 4.1.3 + namespace-emitter: 2.0.1 + + '@uppy/core@2.3.4': + dependencies: + '@transloadit/prettier-bytes': 0.0.7 + '@uppy/store-default': 2.1.1 + '@uppy/utils': 4.1.3 + lodash.throttle: 4.1.1 + mime-match: 1.0.2 + namespace-emitter: 2.0.1 + nanoid: 3.3.7 + preact: 10.22.1 + + '@uppy/store-default@2.1.1': {} + + '@uppy/utils@4.1.3': + dependencies: + lodash.throttle: 4.1.1 + + '@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4)': + dependencies: + '@uppy/companion-client': 2.2.2 + '@uppy/core': 2.3.4 + '@uppy/utils': 4.1.3 + nanoid: 3.3.7 + '@vitejs/plugin-vue-jsx@4.0.0(vite@5.3.2(@types/node@20.14.9)(sass@1.77.6))(vue@3.4.31(typescript@5.5.3))': dependencies: '@babel/core': 7.24.7 @@ -4929,6 +5399,21 @@ snapshots: path-browserify: 1.0.1 vscode-uri: 3.0.8 + '@vue-flow/background@1.3.0(@vue-flow/core@1.38.5(vue@3.4.31(typescript@5.5.3)))(vue@3.4.31(typescript@5.5.3))': + dependencies: + '@vue-flow/core': 1.38.5(vue@3.4.31(typescript@5.5.3)) + vue: 3.4.31(typescript@5.5.3) + + '@vue-flow/core@1.38.5(vue@3.4.31(typescript@5.5.3))': + dependencies: + '@vueuse/core': 10.11.0(vue@3.4.31(typescript@5.5.3)) + d3-drag: 3.0.0 + d3-selection: 3.0.0 + d3-zoom: 3.0.0 + vue: 3.4.31(typescript@5.5.3) + transitivePeerDependencies: + - '@vue/composition-api' + '@vue/babel-helper-vue-transform-on@1.2.2': {} '@vue/babel-plugin-jsx@1.2.2(@babel/core@7.24.7)': @@ -5037,6 +5522,15 @@ snapshots: - '@vue/composition-api' - vue + '@vueuse/core@8.9.4(vue@3.4.31(typescript@5.5.3))': + dependencies: + '@types/web-bluetooth': 0.0.14 + '@vueuse/metadata': 8.9.4 + '@vueuse/shared': 8.9.4(vue@3.4.31(typescript@5.5.3)) + vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) + optionalDependencies: + vue: 3.4.31(typescript@5.5.3) + '@vueuse/core@9.13.0(vue@3.4.31(typescript@5.5.3))': dependencies: '@types/web-bluetooth': 0.0.16 @@ -5049,6 +5543,8 @@ snapshots: '@vueuse/metadata@10.11.0': {} + '@vueuse/metadata@8.9.4': {} + '@vueuse/metadata@9.13.0': {} '@vueuse/motion@2.2.3(rollup@4.18.0)(vue@3.4.31(typescript@5.5.3))': @@ -5075,6 +5571,12 @@ snapshots: - '@vue/composition-api' - vue + '@vueuse/shared@8.9.4(vue@3.4.31(typescript@5.5.3))': + dependencies: + vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) + optionalDependencies: + vue: 3.4.31(typescript@5.5.3) + '@vueuse/shared@9.13.0(vue@3.4.31(typescript@5.5.3))': dependencies: vue-demi: 0.14.8(vue@3.4.31(typescript@5.5.3)) @@ -5082,6 +5584,114 @@ snapshots: - '@vue/composition-api' - vue + '@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + is-url: 1.2.4 + lodash.throttle: 4.1.1 + nanoid: 3.3.7 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/code-highlight@1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + prismjs: 1.29.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@types/event-emitter': 0.3.5 + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + dom7: 3.0.0 + event-emitter: 0.3.5 + html-void-elements: 2.0.1 + i18next: 20.6.1 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.7 + scroll-into-view-if-needed: 2.2.31 + slate: 0.72.8 + slate-history: 0.66.0(slate@0.72.8) + snabbdom: 3.6.2 + + '@wangeditor/editor-for-vue@5.1.12(@wangeditor/editor@5.1.23)(vue@3.4.31(typescript@5.5.3))': + dependencies: + '@wangeditor/editor': 5.1.23 + vue: 3.4.31(typescript@5.5.3) + + '@wangeditor/editor@5.1.23': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/code-highlight': 1.0.3(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/list-module': 1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/table-module': 1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/upload-image-module': 1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/video-module': 1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + is-hotkey: 0.2.0 + lodash.camelcase: 4.3.0 + lodash.clonedeep: 4.5.0 + lodash.debounce: 4.0.8 + lodash.foreach: 4.5.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + lodash.toarray: 4.4.0 + nanoid: 3.3.7 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/list-module@1.0.5(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/table-module@1.1.4(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + lodash.isequal: 4.5.0 + lodash.throttle: 4.1.1 + nanoid: 3.3.7 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/upload-image-module@1.0.2(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/basic-modules@1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.foreach@4.5.0)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/basic-modules': 1.1.7(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(lodash.throttle@4.1.1)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + lodash.foreach: 4.5.0 + slate: 0.72.8 + snabbdom: 3.6.2 + + '@wangeditor/video-module@1.1.4(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(@wangeditor/core@1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2))(dom7@3.0.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2)': + dependencies: + '@uppy/core': 2.3.4 + '@uppy/xhr-upload': 2.1.3(@uppy/core@2.3.4) + '@wangeditor/core': 1.1.19(@uppy/core@2.3.4)(@uppy/xhr-upload@2.1.3(@uppy/core@2.3.4))(dom7@3.0.0)(is-hotkey@0.2.0)(lodash.camelcase@4.3.0)(lodash.clonedeep@4.5.0)(lodash.debounce@4.0.8)(lodash.foreach@4.5.0)(lodash.isequal@4.5.0)(lodash.throttle@4.1.1)(lodash.toarray@4.4.0)(nanoid@3.3.7)(slate@0.72.8)(snabbdom@3.6.2) + dom7: 3.0.0 + nanoid: 3.3.7 + slate: 0.72.8 + snabbdom: 3.6.2 + '@zougt/some-loader-utils@1.4.3': dependencies: cac: 6.7.14 @@ -5093,6 +5703,10 @@ snapshots: prettier: 2.8.8 uuid: 8.3.2 + '@zxcvbn-ts/core@3.0.4': + dependencies: + fastest-levenshtein: 1.0.16 + JSONStream@1.3.5: dependencies: jsonparse: 1.3.1 @@ -5104,6 +5718,8 @@ snapshots: acorn@8.12.0: {} + adler-32@1.3.1: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -5270,6 +5886,11 @@ snapshots: caniuse-lite@1.0.30001639: {} + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 @@ -5320,6 +5941,8 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 + codepage@1.15.0: {} + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -5366,6 +5989,8 @@ snapshots: compatx@0.1.8: optional: true + compute-scroll-into-view@1.0.20: {} + computeds@0.0.1: {} concat-map@0.0.1: {} @@ -5408,6 +6033,8 @@ snapshots: optionalDependencies: typescript: 5.5.3 + crc-32@1.2.2: {} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -5565,6 +6192,47 @@ snapshots: csstype@3.1.3: {} + d3-color@3.1.0: {} + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-ease@3.0.1: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-selection@3.0.0: {} + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d@1.0.2: + dependencies: + es5-ext: 0.10.64 + type: 2.7.3 + dargs@8.1.0: {} dayjs@1.11.11: {} @@ -5613,6 +6281,12 @@ snapshots: domhandler: 5.0.3 entities: 4.5.0 + dom-zindex@1.0.4: {} + + dom7@3.0.0: + dependencies: + ssr-window: 3.0.0 + domelementtype@2.3.0: {} domhandler@4.3.1: @@ -5651,6 +6325,25 @@ snapshots: electron-to-chromium@1.4.816: {} + element-plus@2.1.4(@types/lodash-es@4.17.12)(vue@3.4.31(typescript@5.5.3)): + dependencies: + '@ctrl/tinycolor': 3.6.1 + '@element-plus/icons-vue': 1.1.4(vue@3.4.31(typescript@5.5.3)) + '@popperjs/core': 2.11.8 + '@vueuse/core': 8.9.4(vue@3.4.31(typescript@5.5.3)) + async-validator: 4.2.5 + dayjs: 1.11.11 + escape-html: 1.0.3 + lodash: 4.17.21 + lodash-es: 4.17.21 + lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21) + memoize-one: 6.0.0 + normalize-wheel-es: 1.2.0 + vue: 3.4.31(typescript@5.5.3) + transitivePeerDependencies: + - '@types/lodash-es' + - '@vue/composition-api' + element-plus@2.7.6(vue@3.4.31(typescript@5.5.3)): dependencies: '@ctrl/tinycolor': 3.6.1 @@ -5696,6 +6389,24 @@ snapshots: es-module-lexer@0.4.1: {} + es5-ext@0.10.64: + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.4 + esniff: 2.0.1 + next-tick: 1.1.0 + + es6-iterator@2.0.3: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + es6-symbol: 3.1.4 + + es6-symbol@3.1.4: + dependencies: + d: 1.0.2 + ext: 1.7.0 + esbuild@0.19.12: optionalDependencies: '@esbuild/aix-ppc64': 0.19.12 @@ -5849,6 +6560,13 @@ snapshots: transitivePeerDependencies: - supports-color + esniff@2.0.1: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + event-emitter: 0.3.5 + type: 2.7.3 + espree@10.1.0: dependencies: acorn: 8.12.0 @@ -5881,6 +6599,11 @@ snapshots: esutils@2.0.3: {} + event-emitter@0.3.5: + dependencies: + d: 1.0.2 + es5-ext: 0.10.64 + eventemitter3@5.0.1: {} execa@8.0.1: @@ -5895,6 +6618,10 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + ext@1.7.0: + dependencies: + type: 2.7.3 + fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} @@ -5965,6 +6692,8 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + frac@1.1.2: {} + fraction.js@4.3.7: {} framesync@6.1.2: @@ -6132,6 +6861,8 @@ snapshots: html-tags@3.3.1: {} + html-void-elements@2.0.1: {} + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 @@ -6141,11 +6872,16 @@ snapshots: human-signals@5.0.0: {} + i18next@20.6.1: + dependencies: + '@babel/runtime': 7.24.7 ignore@5.3.1: {} immediate@3.0.6: {} + immer@9.0.21: {} + immutable@4.3.6: {} import-fresh@3.3.0: @@ -6198,6 +6934,8 @@ snapshots: dependencies: is-extglob: 2.1.1 + is-hotkey@0.2.0: {} + is-number@7.0.0: {} is-obj@2.0.0: {} @@ -6216,6 +6954,8 @@ snapshots: dependencies: text-extensions: 2.4.0 + is-url@1.2.4: {} + is-wsl@2.2.0: dependencies: is-docker: 2.2.1 @@ -6352,6 +7092,14 @@ snapshots: lodash.camelcase@4.3.0: {} + lodash.clonedeep@4.5.0: {} + + lodash.debounce@4.0.8: {} + + lodash.foreach@4.5.0: {} + + lodash.isequal@4.5.0: {} + lodash.isplainobject@4.0.6: {} lodash.kebabcase@4.1.1: {} @@ -6366,6 +7114,10 @@ snapshots: lodash.startcase@4.4.0: {} + lodash.throttle@4.1.1: {} + + lodash.toarray@4.4.0: {} + lodash.truncate@4.4.2: {} lodash.uniq@4.5.0: {} @@ -6421,6 +7173,10 @@ snapshots: mime-db@1.52.0: {} + mime-match@1.0.2: + dependencies: + wildcard: 1.1.2 + mime-types@2.1.35: dependencies: mime-db: 1.52.0 @@ -6455,6 +7211,8 @@ snapshots: yallist: 4.0.0 optional: true + mitt@2.1.0: {} + mitt@3.0.1: {} mkdirp@1.0.4: @@ -6480,10 +7238,14 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 + namespace-emitter@2.0.1: {} + nanoid@3.3.7: {} natural-compare@1.4.0: {} + next-tick@1.1.0: {} + node-fetch-native@1.6.4: optional: true @@ -7017,6 +7779,8 @@ snapshots: picocolors: 1.0.1 source-map-js: 1.2.0 + preact@10.22.1: {} + prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.0: @@ -7027,6 +7791,8 @@ snapshots: prettier@3.3.2: {} + prismjs@1.29.0: {} + process@0.11.10: {} proxy-from-env@1.1.0: {} @@ -7053,6 +7819,8 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -7133,6 +7901,10 @@ snapshots: immutable: 4.3.6 source-map-js: 1.2.0 + scroll-into-view-if-needed@2.2.31: + dependencies: + compute-scroll-into-view: 1.0.20 + scule@1.3.0: optional: true @@ -7175,6 +7947,17 @@ snapshots: slash@5.1.0: optional: true + slate-history@0.66.0(slate@0.72.8): + dependencies: + is-plain-object: 5.0.0 + slate: 0.72.8 + + slate@0.72.8: + dependencies: + immer: 9.0.21 + is-plain-object: 5.0.0 + tiny-warning: 1.0.3 + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 @@ -7191,6 +7974,10 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + snabbdom@3.6.2: {} + + sortablejs@1.14.0: {} + sortablejs@1.15.2: {} source-map-js@1.2.0: {} @@ -7203,6 +7990,12 @@ snapshots: split2@4.2.0: {} + ssf@0.11.2: + dependencies: + frac: 1.1.2 + + ssr-window@3.0.0: {} + stable@0.1.8: {} std-env@3.7.0: @@ -7487,6 +8280,8 @@ snapshots: tiny-invariant@1.3.3: {} + tiny-warning@1.0.3: {} + tinycolor2@1.6.0: {} tinygradient@1.1.5: @@ -7526,6 +8321,8 @@ snapshots: type-fest@2.19.0: {} + type@2.7.3: {} + typescript@5.5.3: {} ufo@1.5.3: {} @@ -7743,6 +8540,14 @@ snapshots: '@vue/devtools-api': 6.6.3 vue: 3.4.31(typescript@5.5.3) + vue-observe-visibility@2.0.0-alpha.1(vue@3.4.31(typescript@5.5.3)): + dependencies: + vue: 3.4.31(typescript@5.5.3) + + vue-resize@2.0.0-alpha.1(vue@3.4.31(typescript@5.5.3)): + dependencies: + vue: 3.4.31(typescript@5.5.3) + vue-router@4.4.0(vue@3.4.31(typescript@5.5.3)): dependencies: '@vue/devtools-api': 6.6.3 @@ -7771,6 +8576,29 @@ snapshots: optionalDependencies: vue: 3.4.31(typescript@5.5.3) + vue-virtual-scroller@2.0.0-beta.8(vue@3.4.31(typescript@5.5.3)): + dependencies: + mitt: 2.1.0 + vue: 3.4.31(typescript@5.5.3) + vue-observe-visibility: 2.0.0-alpha.1(vue@3.4.31(typescript@5.5.3)) + vue-resize: 2.0.0-alpha.1(vue@3.4.31(typescript@5.5.3)) + + vue-waterfall-plugin-next@2.4.3(@types/lodash-es@4.17.12)(vue@3.4.31(typescript@5.5.3)): + dependencies: + animate.css: 4.1.1 + element-plus: 2.1.4(@types/lodash-es@4.17.12)(vue@3.4.31(typescript@5.5.3)) + vue-router: 4.4.0(vue@3.4.31(typescript@5.5.3)) + transitivePeerDependencies: + - '@types/lodash-es' + - '@vue/composition-api' + - vue + + vue3-danmaku@1.6.0(vue@3.4.31(typescript@5.5.3)): + dependencies: + vue: 3.4.31(typescript@5.5.3) + + vue3-puzzle-vcode@1.1.7: {} + vue@3.4.31(typescript@5.5.3): dependencies: '@vue/compiler-dom': 3.4.31 @@ -7781,6 +8609,17 @@ snapshots: optionalDependencies: typescript: 5.5.3 + vuedraggable@4.1.0(vue@3.4.31(typescript@5.5.3)): + dependencies: + sortablejs: 1.14.0 + vue: 3.4.31(typescript@5.5.3) + + vxe-table@4.6.17(vue@3.4.31(typescript@5.5.3)): + dependencies: + dom-zindex: 1.0.4 + vue: 3.4.31(typescript@5.5.3) + xe-utils: 3.5.28 + webpack-sources@3.2.3: {} webpack-virtual-modules@0.6.2: {} @@ -7797,8 +8636,14 @@ snapshots: dependencies: string-width: 5.1.2 + wildcard@1.1.2: {} + + wmf@1.0.2: {} + word-wrap@1.2.5: {} + word@0.3.0: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -7822,6 +8667,18 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 4.1.0 + xe-utils@3.5.28: {} + + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + xml-name-validator@4.0.0: {} y18n@5.0.8: {} diff --git a/public/platform-config.json b/public/platform-config.json index 458f204..bc4f8e3 100644 --- a/public/platform-config.json +++ b/public/platform-config.json @@ -5,7 +5,7 @@ "HiddenSideBar": false, "MultiTagsCache": false, "KeepAlive": true, - "Locale": "zh", + "Locale": "vi", "Layout": "vertical", "Theme": "light", "DarkMode": false, diff --git a/src/App.vue b/src/App.vue index 9204c7c..b698cc2 100644 --- a/src/App.vue +++ b/src/App.vue @@ -10,7 +10,7 @@ import { defineComponent } from "vue"; import { ElConfigProvider } from "element-plus"; import { ReDialog } from "@/components/ReDialog"; import en from "element-plus/es/locale/lang/en"; -import zhCn from "element-plus/es/locale/lang/zh-cn"; +import viVN from "element-plus/es/locale/lang/vi"; export default defineComponent({ name: "app", @@ -20,7 +20,7 @@ export default defineComponent({ }, computed: { currentLocale() { - return this.$storage.locale?.locale === "zh" ? zhCn : en; + return this.$storage.locale?.locale === "vi" ? viVN : en; } } }); diff --git a/src/api/routes.ts b/src/api/routes.ts index 501ea3c..2526dc4 100644 --- a/src/api/routes.ts +++ b/src/api/routes.ts @@ -2,7 +2,9 @@ import { http } from "@/utils/http"; type Result = { success: boolean; - data: Array; + msg: string; + msgDev: string; + data: object; }; export const getAsyncRoutes = () => { diff --git a/src/api/user.ts b/src/api/user.ts index ab100b5..36479a4 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -3,19 +3,19 @@ import { http } from "@/utils/http"; export type UserResult = { success: boolean; data: { - /** 头像 */ + /** Đường dẫn ảnh đại diện */ avatar: string; - /** 用户名 */ + /** Tên người dùng */ username: string; - /** 昵称 */ + /** Bút danh */ nickname: string; - /** 当前登录用户的角色 */ + /** Các vai trò của người dùng hiện tại */ roles: Array; - /** `token` */ + /** Mã thông báo truy cập */ accessToken: string; - /** 用于调用刷新`accessToken`的接口时所需的`token` */ + /** Mã thông báo cần thiết để gọi API làm mới `accessToken` */ refreshToken: string; - /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */ + /** Thời gian hết hạn của `accessToken` (định dạng 'xxxx/xx/xx xx:xx:xx') */ expires: Date; }; }; @@ -23,21 +23,21 @@ export type UserResult = { export type RefreshTokenResult = { success: boolean; data: { - /** `token` */ + /** Mã thông báo truy cập */ accessToken: string; - /** 用于调用刷新`accessToken`的接口时所需的`token` */ + /** Mã thông báo cần thiết để gọi API làm mới `accessToken` */ refreshToken: string; - /** `accessToken`的过期时间(格式'xxxx/xx/xx xx:xx:xx') */ + /** Thời gian hết hạn của `accessToken` (định dạng 'xxxx/xx/xx xx:xx:xx') */ expires: Date; }; }; -/** 登录 */ +/** Đăng nhập */ export const getLogin = (data?: object) => { return http.request("post", "/login", { data }); }; -/** 刷新`token` */ +/** Làm mới `token` */ export const refreshTokenApi = (data?: object) => { return http.request("post", "/refresh-token", { data }); }; diff --git a/src/components/ReCol/index.ts b/src/components/ReCol/index.ts index 7a6c937..536e0a7 100644 --- a/src/components/ReCol/index.ts +++ b/src/components/ReCol/index.ts @@ -1,7 +1,7 @@ import { ElCol } from "element-plus"; import { h, defineComponent } from "vue"; -// 封装element-plus的el-col组件 +// Bao bọc thành phần ElCol của element-plus export default defineComponent({ name: "ReCol", props: { diff --git a/src/components/ReCountTo/README.md b/src/components/ReCountTo/README.md new file mode 100644 index 0000000..b5048f3 --- /dev/null +++ b/src/components/ReCountTo/README.md @@ -0,0 +1,2 @@ +normal 普通数字动画组件 +rebound 回弹式数字动画组件 diff --git a/src/components/ReCountTo/index.ts b/src/components/ReCountTo/index.ts new file mode 100644 index 0000000..1817218 --- /dev/null +++ b/src/components/ReCountTo/index.ts @@ -0,0 +1,11 @@ +import reNormalCountTo from "./src/normal"; +import reboundCountTo from "./src/rebound"; +import { withInstall } from "@pureadmin/utils"; + +/** 普通数字动画组件 */ +const ReNormalCountTo = withInstall(reNormalCountTo); + +/** 回弹式数字动画组件 */ +const ReboundCountTo = withInstall(reboundCountTo); + +export { ReNormalCountTo, ReboundCountTo }; diff --git a/src/components/ReCountTo/src/normal/index.tsx b/src/components/ReCountTo/src/normal/index.tsx new file mode 100644 index 0000000..538e0bb --- /dev/null +++ b/src/components/ReCountTo/src/normal/index.tsx @@ -0,0 +1,179 @@ +import { + watch, + unref, + computed, + reactive, + onMounted, + defineComponent +} from "vue"; +import { countToProps } from "./props"; +import { isNumber } from "@pureadmin/utils"; + +export default defineComponent({ + name: "ReNormalCountTo", + props: countToProps, + emits: ["mounted", "callback"], + setup(props, { emit }) { + const state = reactive<{ + localStartVal: number; + printVal: number | null; + displayValue: string; + paused: boolean; + localDuration: number | null; + startTime: number | null; + timestamp: number | null; + rAF: any; + remaining: number | null; + color: string; + fontSize: string; + }>({ + localStartVal: props.startVal, + displayValue: formatNumber(props.startVal), + printVal: null, + paused: false, + localDuration: props.duration, + startTime: null, + timestamp: null, + remaining: null, + rAF: null, + color: null, + fontSize: "16px" + }); + + const getCountDown = computed(() => { + return props.startVal > props.endVal; + }); + + watch([() => props.startVal, () => props.endVal], () => { + if (props.autoplay) { + start(); + } + }); + + function start() { + const { startVal, duration, color, fontSize } = props; + state.localStartVal = startVal; + state.startTime = null; + state.localDuration = duration; + state.paused = false; + state.color = color; + state.fontSize = fontSize; + state.rAF = requestAnimationFrame(count); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function pauseResume() { + if (state.paused) { + resume(); + state.paused = false; + } else { + pause(); + state.paused = true; + } + } + + function pause() { + cancelAnimationFrame(state.rAF); + } + + function resume() { + state.startTime = null; + state.localDuration = +(state.remaining as number); + state.localStartVal = +(state.printVal as number); + requestAnimationFrame(count); + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + function reset() { + state.startTime = null; + cancelAnimationFrame(state.rAF); + state.displayValue = formatNumber(props.startVal); + } + + function count(timestamp: number) { + const { useEasing, easingFn, endVal } = props; + if (!state.startTime) state.startTime = timestamp; + state.timestamp = timestamp; + const progress = timestamp - state.startTime; + state.remaining = (state.localDuration as number) - progress; + if (useEasing) { + if (unref(getCountDown)) { + state.printVal = + state.localStartVal - + easingFn( + progress, + 0, + state.localStartVal - endVal, + state.localDuration as number + ); + } else { + state.printVal = easingFn( + progress, + state.localStartVal, + endVal - state.localStartVal, + state.localDuration as number + ); + } + } else { + if (unref(getCountDown)) { + state.printVal = + state.localStartVal - + (state.localStartVal - endVal) * + (progress / (state.localDuration as number)); + } else { + state.printVal = + state.localStartVal + + (endVal - state.localStartVal) * + (progress / (state.localDuration as number)); + } + } + if (unref(getCountDown)) { + state.printVal = state.printVal < endVal ? endVal : state.printVal; + } else { + state.printVal = state.printVal > endVal ? endVal : state.printVal; + } + state.displayValue = formatNumber(state.printVal); + if (progress < (state.localDuration as number)) { + state.rAF = requestAnimationFrame(count); + } else { + emit("callback"); + } + } + + function formatNumber(num: number | string) { + const { decimals, decimal, separator, suffix, prefix } = props; + num = Number(num).toFixed(decimals); + num += ""; + const x = num.split("."); + let x1 = x[0]; + const x2 = x.length > 1 ? decimal + x[1] : ""; + const rgx = /(\d+)(\d{3})/; + if (separator && !isNumber(separator)) { + while (rgx.test(x1)) { + x1 = x1.replace(rgx, "$1" + separator + "$2"); + } + } + return prefix + x1 + x2 + suffix; + } + + onMounted(() => { + if (props.autoplay) { + start(); + } + emit("mounted"); + }); + + return () => ( + <> + + {state.displayValue} + + + ); + } +}); diff --git a/src/components/ReCountTo/src/normal/props.ts b/src/components/ReCountTo/src/normal/props.ts new file mode 100644 index 0000000..f04c2f5 --- /dev/null +++ b/src/components/ReCountTo/src/normal/props.ts @@ -0,0 +1,32 @@ +import type { PropType } from "vue"; +import propTypes from "@/utils/propTypes"; + +export const countToProps = { + startVal: propTypes.number.def(0), + endVal: propTypes.number.def(2020), + duration: propTypes.number.def(1300), + autoplay: propTypes.bool.def(true), + decimals: { + type: Number as PropType, + required: false, + default: 0, + validator(value: number) { + return value >= 0; + } + }, + color: propTypes.string.def(), + fontSize: propTypes.string.def(), + decimal: propTypes.string.def("."), + separator: propTypes.string.def(","), + prefix: propTypes.string.def(""), + suffix: propTypes.string.def(""), + useEasing: propTypes.bool.def(true), + easingFn: { + type: Function as PropType< + (t: number, b: number, c: number, d: number) => number + >, + default(t: number, b: number, c: number, d: number) { + return (c * (-Math.pow(2, (-10 * t) / d) + 1) * 1024) / 1023 + b; + } + } +}; diff --git a/src/components/ReCountTo/src/rebound/index.tsx b/src/components/ReCountTo/src/rebound/index.tsx new file mode 100644 index 0000000..ff6c224 --- /dev/null +++ b/src/components/ReCountTo/src/rebound/index.tsx @@ -0,0 +1,72 @@ +import "./rebound.css"; +import { + ref, + unref, + onBeforeMount, + defineComponent, + onBeforeUnmount +} from "vue"; +import { reboundProps } from "./props"; + +export default defineComponent({ + name: "ReboundCountTo", + props: reboundProps, + setup(props) { + const ulRef = ref(); + const timer = ref(null); + + onBeforeMount(() => { + const ua = navigator.userAgent.toLowerCase(); + const testUA = regexp => regexp.test(ua); + const isSafari = testUA(/safari/g) && !testUA(/chrome/g); + + // Safari浏览器的兼容代码 + isSafari && + (timer.value = setTimeout(() => { + ulRef.value.setAttribute( + "style", + ` + animation: none; + transform: translateY(calc(var(--i) * -9.09%)) + ` + ); + }, props.delay * 1000)); + }); + + onBeforeUnmount(() => { + clearTimeout(unref(timer)); + }); + + return () => ( + <> +
+
    +
  • 0
  • +
  • 1
  • +
  • 2
  • +
  • 3
  • +
  • 4
  • +
  • 5
  • +
  • 6
  • +
  • 7
  • +
  • 8
  • +
  • 9
  • +
  • 0
  • +
+ + + + + + +
+ + ); + } +}); diff --git a/src/components/ReCountTo/src/rebound/props.ts b/src/components/ReCountTo/src/rebound/props.ts new file mode 100644 index 0000000..8b0491a --- /dev/null +++ b/src/components/ReCountTo/src/rebound/props.ts @@ -0,0 +1,15 @@ +import type { PropType } from "vue"; +import propTypes from "@/utils/propTypes"; + +export const reboundProps = { + delay: propTypes.number.def(1), + blur: propTypes.number.def(2), + i: { + type: Number as PropType, + required: false, + default: 0, + validator(value: number) { + return value < 10 && value >= 0 && Number.isInteger(value); + } + } +}; diff --git a/src/components/ReCountTo/src/rebound/rebound.css b/src/components/ReCountTo/src/rebound/rebound.css new file mode 100644 index 0000000..9fc5932 --- /dev/null +++ b/src/components/ReCountTo/src/rebound/rebound.css @@ -0,0 +1,77 @@ +.scroll-num { + width: var(--width, 20px); + height: var(--height, calc(var(--width, 20px) * 1.8)); + color: var(--color, #333); + font-size: var(--height, calc(var(--width, 20px) * 1.1)); + line-height: var(--height, calc(var(--width, 20px) * 1.8)); + text-align: center; + overflow: hidden; + animation: enhance-bounce-in-down 1s calc(var(--delay) * 1s) forwards; +} + +ul { + animation: + move 0.3s linear infinite, + bounce-in-down 1s calc(var(--delay) * 1s) forwards; +} + +@keyframes move { + from { + transform: translateY(-90%); + filter: url(#blur); + } + + to { + transform: translateY(1%); + filter: url(#blur); + } +} + +@keyframes bounce-in-down { + from { + transform: translateY(calc(var(--i) * -9.09% - 7%)); + filter: none; + } + + 25% { + transform: translateY(calc(var(--i) * -9.09% + 3%)); + } + + 50% { + transform: translateY(calc(var(--i) * -9.09% - 1%)); + } + + 70% { + transform: translateY(calc(var(--i) * -9.09% + 0.6%)); + } + + 85% { + transform: translateY(calc(var(--i) * -9.09% - 0.3%)); + } + + to { + transform: translateY(calc(var(--i) * -9.09%)); + } +} + +@keyframes enhance-bounce-in-down { + 25% { + transform: translateY(8%); + } + + 50% { + transform: translateY(-4%); + } + + 70% { + transform: translateY(2%); + } + + 85% { + transform: translateY(-1%); + } + + to { + transform: translateY(0); + } +} diff --git a/src/components/ReDialog/index.ts b/src/components/ReDialog/index.ts index b471764..661c539 100644 --- a/src/components/ReDialog/index.ts +++ b/src/components/ReDialog/index.ts @@ -12,7 +12,7 @@ import type { const dialogStore = ref>([]); -/** 打开弹框 */ +/** Mở hộp thoại */ const addDialog = (options: DialogOptions) => { const open = () => dialogStore.value.push(Object.assign(options, { visible: true })); @@ -25,7 +25,7 @@ const addDialog = (options: DialogOptions) => { } }; -/** 关闭弹框 */ +/** Đóng hộp thoại */ const closeDialog = (options: DialogOptions, index: number, args?: any) => { dialogStore.value[index].visible = false; options.closeCallBack && options.closeCallBack({ options, index, args }); @@ -37,21 +37,21 @@ const closeDialog = (options: DialogOptions, index: number, args?: any) => { }; /** - * @description 更改弹框自身属性值 - * @param value 属性值 - * @param key 属性,默认`title` - * @param index 弹框索引(默认`0`,代表只有一个弹框,对于嵌套弹框要改哪个弹框的属性值就把该弹框索引赋给`index`) + * @description Thay đổi giá trị thuộc tính của hộp thoại + * @param value Giá trị thuộc tính + * @param key Thuộc tính (mặc định là `title`) + * @param index Chỉ số của hộp thoại (mặc định là `0`, đại diện cho hộp thoại duy nhất; để thay đổi thuộc tính của hộp thoại lồng nhau, gán chỉ số của hộp thoại đó cho `index`) */ const updateDialog = (value: any, key = "title", index = 0) => { dialogStore.value[index][key] = value; }; -/** 关闭所有弹框 */ +/** Đóng tất cả hộp thoại */ const closeAllDialog = () => { dialogStore.value = []; }; -/** 千万别忘了在下面这三处引入并注册下,放心注册,不使用`addDialog`调用就不会被挂载 +/** Chắc chắn phải nhập và đăng ký ở ba dưới đây, yên tâm đăng ký, không sử dụng `addDialog` thì sẽ không bị gắn kết * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L4 * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L12 * https://github.com/pure-admin/vue-pure-admin/blob/main/src/App.vue#L22 diff --git a/src/components/ReDialog/index.vue b/src/components/ReDialog/index.vue index 1db105f..2b37f38 100644 --- a/src/components/ReDialog/index.vue +++ b/src/components/ReDialog/index.vue @@ -23,7 +23,7 @@ const footerButtons = computed(() => { ? options.footerButtons : ([ { - label: "取消", + label: "Hủy", text: true, bg: true, btnClick: ({ dialog: { options, index } }) => { @@ -37,7 +37,7 @@ const footerButtons = computed(() => { } }, { - label: "确定", + label: "OK", type: "primary", text: true, bg: true, diff --git a/src/components/ReFlicker/index.css b/src/components/ReFlicker/index.css new file mode 100644 index 0000000..4c40af4 --- /dev/null +++ b/src/components/ReFlicker/index.css @@ -0,0 +1,39 @@ +.point { + width: var(--point-width); + height: var(--point-height); + background: var(--point-background); + position: relative; + border-radius: var(--point-border-radius); +} + +.point-flicker:after { + background: var(--point-background); +} + +.point-flicker:before, +.point-flicker:after { + content: ""; + width: 100%; + height: 100%; + top: 0; + left: 0; + position: absolute; + border-radius: var(--point-border-radius); + animation: flicker 1.2s ease-out infinite; +} + +@keyframes flicker { + 0% { + transform: scale(0.5); + opacity: 1; + } + + 30% { + opacity: 1; + } + + 100% { + transform: scale(var(--point-scale)); + opacity: 0; + } +} diff --git a/src/components/ReFlicker/index.ts b/src/components/ReFlicker/index.ts new file mode 100644 index 0000000..b829323 --- /dev/null +++ b/src/components/ReFlicker/index.ts @@ -0,0 +1,44 @@ +import "./index.css"; +import { type Component, h, defineComponent } from "vue"; + +export interface attrsType { + width?: string; + height?: string; + borderRadius?: number | string; + background?: string; + scale?: number | string; +} + +/** + * 圆点、方形闪烁动画组件 + * @param width 可选 string 宽 + * @param height 可选 string 高 + * @param borderRadius 可选 number | string 传0为方形、传50%或者不传为圆形 + * @param background 可选 string 闪烁颜色 + * @param scale 可选 number | string 闪烁范围,默认2,值越大闪烁范围越大 + * @returns Component + */ +export function useRenderFlicker(attrs?: attrsType): Component { + return defineComponent({ + name: "ReFlicker", + render() { + return h( + "div", + { + class: "point point-flicker", + style: { + "--point-width": attrs?.width ?? "12px", + "--point-height": attrs?.height ?? "12px", + "--point-background": + attrs?.background ?? "var(--el-color-primary)", + "--point-border-radius": attrs?.borderRadius ?? "50%", + "--point-scale": attrs?.scale ?? "2" + } + }, + { + default: () => [] + } + ); + } + }); +} diff --git a/src/components/ReIcon/index.ts b/src/components/ReIcon/index.ts index 86efe72..3d9225e 100644 --- a/src/components/ReIcon/index.ts +++ b/src/components/ReIcon/index.ts @@ -2,11 +2,11 @@ import iconifyIconOffline from "./src/iconifyIconOffline"; import iconifyIconOnline from "./src/iconifyIconOnline"; import fontIcon from "./src/iconfont"; -/** 本地图标组件 */ +/** icon offline*/ const IconifyIconOffline = iconifyIconOffline; -/** 在线图标组件 */ +/** icon online */ const IconifyIconOnline = iconifyIconOnline; -/** `iconfont`组件 */ +/** `iconfont` */ const FontIcon = fontIcon; export { IconifyIconOffline, IconifyIconOnline, FontIcon }; diff --git a/src/components/ReIcon/src/hooks.ts b/src/components/ReIcon/src/hooks.ts index 5a377da..b13fad5 100644 --- a/src/components/ReIcon/src/hooks.ts +++ b/src/components/ReIcon/src/hooks.ts @@ -3,16 +3,16 @@ import { h, defineComponent, type Component } from "vue"; import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index"; /** - * 支持 `iconfont`、自定义 `svg` 以及 `iconify` 中所有的图标 - * @see 点击查看文档图标篇 {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/} - * @param icon 必传 图标 - * @param attrs 可选 iconType 属性 + * Hỗ trợ `iconfont`, `svg` tùy chỉnh và tất cả các biểu tượng trong `iconify` + * @see Nhấp để xem hướng dẫn biểu tượng trong tài liệu {@link https://pure-admin.github.io/pure-admin-doc/pages/icon/} + * @param icon Bắt buộc Biểu tượng + * @param attrs Tùy chọn Các thuộc tính loại iconType * @returns Component */ export function useRenderIcon(icon: any, attrs?: iconType): Component { // iconfont const ifReg = /^IF-/; - // typeof icon === "function" 属于SVG + // typeof icon === "function" belongs to SVG if (ifReg.test(icon)) { // iconfont const name = icon.split(ifReg)[1]; @@ -45,7 +45,7 @@ export function useRenderIcon(icon: any, attrs?: iconType): Component { } }); } else { - // 通过是否存在 : 符号来判断是在线还是本地图标,存在即是在线图标,反之 + // Kiểm tra dấu : để xác định biểu tượng trực tuyến hoặc cục bộ, có tồn tại thì là biểu tượng trực tuyến, ngược lại là cục bộ return defineComponent({ name: "Icon", render() { diff --git a/src/components/ReIcon/src/iconfont.ts b/src/components/ReIcon/src/iconfont.ts index c110451..f356dbe 100644 --- a/src/components/ReIcon/src/iconfont.ts +++ b/src/components/ReIcon/src/iconfont.ts @@ -1,6 +1,6 @@ import { h, defineComponent } from "vue"; -// 封装iconfont组件,默认`font-class`引用模式,支持`unicode`引用、`font-class`引用、`symbol`引用 (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code) +// Đóng gói thành phần iconfont, mặc định sử dụng chế độ tham chiếu `font-class`, hỗ trợ tham chiếu `unicode`, `font-class`, `symbol` (https://www.iconfont.cn/help/detail?spm=a313x.7781069.1998910419.20&helptype=code) export default defineComponent({ name: "FontIcon", props: { diff --git a/src/components/ReIcon/src/iconifyIconOffline.ts b/src/components/ReIcon/src/iconifyIconOffline.ts index b47aa99..6976afa 100644 --- a/src/components/ReIcon/src/iconifyIconOffline.ts +++ b/src/components/ReIcon/src/iconifyIconOffline.ts @@ -1,7 +1,7 @@ import { h, defineComponent } from "vue"; import { Icon as IconifyIcon, addIcon } from "@iconify/vue/dist/offline"; -// Iconify Icon在Vue里本地使用(用于内网环境) +// Biểu tượng Iconify được sử dụng cục bộ trong Vue (dành cho môi trường mạng nội bộ) export default defineComponent({ name: "IconifyIconOffline", components: { IconifyIcon }, diff --git a/src/components/ReIcon/src/iconifyIconOnline.ts b/src/components/ReIcon/src/iconifyIconOnline.ts index a5f5822..f613ef1 100644 --- a/src/components/ReIcon/src/iconifyIconOnline.ts +++ b/src/components/ReIcon/src/iconifyIconOnline.ts @@ -1,7 +1,7 @@ import { h, defineComponent } from "vue"; import { Icon as IconifyIcon } from "@iconify/vue"; -// Iconify Icon在Vue里在线使用(用于外网环境) +// Biểu tượng Iconify được sử dụng cục bộ trong Vue (dành cho môi trường mạng) export default defineComponent({ name: "IconifyIconOnline", components: { IconifyIcon }, diff --git a/src/components/ReIcon/src/offlineIcon.ts b/src/components/ReIcon/src/offlineIcon.ts index fc5f912..36c61fe 100644 --- a/src/components/ReIcon/src/offlineIcon.ts +++ b/src/components/ReIcon/src/offlineIcon.ts @@ -1,12 +1,14 @@ -// 这里存放本地图标,在 src/layout/index.vue 文件中加载,避免在首启动加载 +// Đây là nơi lưu trữ biểu tượng cục bộ, được tải trong tệp src/layout/index.vue để tránh tải lại khi khởi động lần đầu + import { addIcon } from "@iconify/vue/dist/offline"; -// 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标 +// Biểu tượng menu cục bộ, máy chủ trả về chuỗi biểu tượng tương ứng trong thuộc tính icon của định tuyến và phía trước sử dụng addIcon để thêm vào đây để hiển thị biểu tượng menu // @iconify-icons/ep import Lollipop from "@iconify-icons/ep/lollipop"; import HomeFilled from "@iconify-icons/ep/home-filled"; addIcon("ep:lollipop", Lollipop); addIcon("ep:home-filled", HomeFilled); + // @iconify-icons/ri import Search from "@iconify-icons/ri/search-line"; import InformationLine from "@iconify-icons/ri/information-line"; diff --git a/src/components/ReIcon/src/types.ts b/src/components/ReIcon/src/types.ts index 000bdc5..999ed3b 100644 --- a/src/components/ReIcon/src/types.ts +++ b/src/components/ReIcon/src/types.ts @@ -13,7 +13,7 @@ export interface iconType { align?: string; onLoad?: Function; includes?: Function; - // svg 需要什么SVG属性自行添加 + // svg Những thuộc tính SVG nào là cần thiết để bạn thêm vào? fill?: string; // all icon style?: object; diff --git a/src/components/RePureTableBar/index.ts b/src/components/RePureTableBar/index.ts index 31b8a16..748f0e1 100644 --- a/src/components/RePureTableBar/index.ts +++ b/src/components/RePureTableBar/index.ts @@ -1,5 +1,5 @@ import pureTableBar from "./src/bar"; import { withInstall } from "@pureadmin/utils"; -/** 配合 `@pureadmin/table` 实现快速便捷的表格操作 https://github.com/pure-admin/pure-admin-table */ +/** dùng `@pureadmin/table` để thực hiện các thao tác trên bảng nhanh chóng và thuận tiện https://github.com/pure-admin/pure-admin-table */ export const PureTableBar = withInstall(pureTableBar); diff --git a/src/components/RePureTableBar/src/bar.tsx b/src/components/RePureTableBar/src/bar.tsx index 7f5efd1..4813431 100644 --- a/src/components/RePureTableBar/src/bar.tsx +++ b/src/components/RePureTableBar/src/bar.tsx @@ -25,24 +25,26 @@ import SettingIcon from "@/assets/table-bar/settings.svg?component"; import CollapseIcon from "@/assets/table-bar/collapse.svg?component"; const props = { - /** 头部最左边的标题 */ + /** Tiêu đề ở phía trên bên trái của bảng */ title: { type: String, - default: "列表" + default: "Danh sách" }, - /** 对于树形表格,如果想启用展开和折叠功能,传入当前表格的ref即可 */ + /** Nếu muốn bật chức năng mở rộng và thu gọn cho bảng cây, hãy truyền vào tham chiếu hiện tại của bảng */ tableRef: { type: Object as PropType }, - /** 需要展示的列 */ + /** Các cột cần hiển thị */ columns: { type: Array as PropType, default: () => [] }, + /** Cho biết có mở rộng tất cả các hàng ban đầu hay không */ isExpandAll: { type: Boolean, default: true }, + /** Khóa duy nhất cho bảng */ tableKey: { type: [String, Number] as PropType, default: "0" @@ -161,25 +163,25 @@ export default defineComponent({ style={getDropdownItemStyle.value("large")} onClick={() => (size.value = "large")} > - 宽松 + Lỏng lẻo (size.value = "default")} > - 默认 + Mặc định (size.value = "small")} > - 紧凑 + Gọn nhẹ ) }; - /** 列展示拖拽排序 */ + /** Cột hiển thị kéo và thả sắp xếp */ const rowDrop = (event: { preventDefault: () => void }) => { event.preventDefault(); nextTick(() => { @@ -195,7 +197,7 @@ export default defineComponent({ const oldColumn = dynamicColumns.value[oldIndex]; const newColumn = dynamicColumns.value[newIndex]; if (oldColumn?.fixed || newColumn?.fixed) { - // 当前列存在fixed属性 则不可拖拽 + // Nếu cột hiện tại có thuộc tính cố định thì không thể kéo được. const oldThElem = wrapperElem.children[oldIndex] as HTMLElement; if (newIndex > oldIndex) { wrapperElem.insertBefore(targetThElem, oldThElem); @@ -237,7 +239,7 @@ export default defineComponent({ reference: () => ( ) }; @@ -263,7 +265,7 @@ export default defineComponent({ transform: isExpandAll.value ? "none" : "rotate(-90deg)" }} v-tippy={rendTippyProps( - isExpandAll.value ? "折叠" : "展开" + isExpandAll.value ? "Thu nhỏ" : "Mở rộng" )} onClick={() => onExpand()} /> @@ -276,14 +278,14 @@ export default defineComponent({ iconClass.value, loading.value ? "animate-spin" : "" ]} - v-tippy={rendTippyProps("刷新")} + v-tippy={rendTippyProps("Đặt lại")} onClick={() => onReFresh()} /> @@ -299,13 +301,13 @@ export default defineComponent({
handleCheckAllChange(value)} /> onReset()}> - 重置 + Đặt lại
diff --git a/src/components/ReSegmented/index.ts b/src/components/ReSegmented/index.ts index de4253c..fe401dd 100644 --- a/src/components/ReSegmented/index.ts +++ b/src/components/ReSegmented/index.ts @@ -1,7 +1,7 @@ import reSegmented from "./src/index"; import { withInstall } from "@pureadmin/utils"; -/** 分段控制器组件 */ +/** Thành phần điều khiển phân đoạn */ export const ReSegmented = withInstall(reSegmented); export default ReSegmented; diff --git a/src/components/ReSegmented/src/index.tsx b/src/components/ReSegmented/src/index.tsx index 39580ed..e386d33 100644 --- a/src/components/ReSegmented/src/index.tsx +++ b/src/components/ReSegmented/src/index.tsx @@ -23,27 +23,27 @@ const props = { type: Array, default: () => [] }, - /** 默认选中,按照第一个索引为 `0` 的模式,可选(`modelValue`只有传`number`类型时才为响应式) */ + /** Mặc định được chọn, theo mô hình chỉ số đầu tiên là `0`, có thể chọn (Khi `modelValue` chỉ có kiểu `number` thì mới có sự phản hồi) */ modelValue: { type: undefined, require: false, default: "0" }, - /** 将宽度调整为父元素宽度 */ + /** Thay đổi chiều rộng để phù hợp với chiều rộng của phần tử cha */ block: { type: Boolean, default: false }, - /** 控件尺寸 */ + /** Kích thước của điều khiển */ size: { type: String as PropType<"small" | "default" | "large"> }, - /** 是否全局禁用,默认 `false` */ + /** Vô hiệu hóa toàn cầu, mặc định `false` */ disabled: { type: Boolean, default: false }, - /** 当内容发生变化时,设置 `resize` 可使其自适应容器位置 */ + /** Khi nội dung thay đổi, thiết lập `resize` để tự điều chỉnh vị trí của nó trong bộ chứa */ resize: { type: Boolean, default: false diff --git a/src/components/ReSegmented/src/type.ts b/src/components/ReSegmented/src/type.ts index 205e34d..ca757da 100644 --- a/src/components/ReSegmented/src/type.ts +++ b/src/components/ReSegmented/src/type.ts @@ -2,19 +2,19 @@ import type { VNode, Component } from "vue"; import type { iconType } from "@/components/ReIcon/src/types.ts"; export interface OptionsType { - /** 文字 */ + /** Nhãn */ label?: string | (() => VNode | Component); /** - * @description 图标,采用平台内置的 `useRenderIcon` 函数渲染 - * @see {@link 用法参考 https://pure-admin.github.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks } + * @description Biểu tượng, được render bằng hàm `useRenderIcon` được tích hợp trong nền tảng + * @see {@link Xem thêm tại https://pure-admin.github.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks } */ icon?: string | Component; - /** 图标属性、样式配置 */ + /** Thuộc tính và style của biểu tượng */ iconAttrs?: iconType; - /** 值 */ + /** Giá trị */ value?: any; - /** 是否禁用 */ + /** Đã bị vô hiệu hóa hay chưa */ disabled?: boolean; - /** `tooltip` 提示 */ + /** Chú thích tooltip */ tip?: string; } diff --git a/src/components/ReText/index.ts b/src/components/ReText/index.ts index 6213566..8e0bd1e 100644 --- a/src/components/ReText/index.ts +++ b/src/components/ReText/index.ts @@ -1,7 +1,7 @@ import reText from "./src/index.vue"; import { withInstall } from "@pureadmin/utils"; -/** 支持`Tooltip`提示的文本省略组件 */ +/** Thành phần cắt ngắn văn bản hỗ trợ Tooltip */ export const ReText = withInstall(reText); export default ReText; diff --git a/src/components/ReText/src/index.vue b/src/components/ReText/src/index.vue index ecaebdb..0513159 100644 --- a/src/components/ReText/src/index.vue +++ b/src/components/ReText/src/index.vue @@ -7,7 +7,7 @@ defineOptions({ }); const props = defineProps({ - // 行数 + // Số dòng lineClamp: { type: [String, Number] }, @@ -24,10 +24,10 @@ const tippyFunc = ref(); const isTextEllipsis = (el: HTMLElement) => { if (!props.lineClamp) { - // 单行省略判断 + // Kiểm tra có dòng đơn bị cắt ngắn return el.scrollWidth > el.clientWidth; } else { - // 多行省略判断 + // Kiểm tra có dòng nhiều bị cắt ngắn return el.scrollHeight > el.clientHeight; } }; diff --git a/src/config/index.ts b/src/config/index.ts index c81d1c4..f91a3b1 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -26,7 +26,7 @@ const getConfig = (key?: string): PlatformConfigs => { return config; }; -/** 获取项目动态全局配置 */ +/** Lấy cấu hình toàn cầu động của dự án */ export const getPlatformConfig = async (app: App): Promise => { app.config.globalProperties.$config = getConfig(); return axios({ @@ -35,21 +35,21 @@ export const getPlatformConfig = async (app: App): Promise => { }) .then(({ data: config }) => { let $config = app.config.globalProperties.$config; - // 自动注入系统配置 + // Tự động chèn cấu hình hệ thống if (app && $config && typeof config === "object") { $config = Object.assign($config, config); app.config.globalProperties.$config = $config; - // 设置全局配置 + // Đặt cấu hình toàn cầu setConfig($config); } return $config; }) .catch(() => { - throw "请在public文件夹下添加platform-config.json配置文件"; + throw "Vui lòng thêm tệp cấu hình platform-config.json trong thư mục public"; }); }; -/** 本地响应式存储的命名空间 */ +/** Không gian lưu trữ đáp ứng cục bộ */ const responsiveStorageNameSpace = () => getConfig().ResponsiveStorageNameSpace; export { getConfig, setConfig, responsiveStorageNameSpace }; diff --git a/src/directives/copy/index.ts b/src/directives/copy/index.ts index b71fa19..f8d1ea1 100644 --- a/src/directives/copy/index.ts +++ b/src/directives/copy/index.ts @@ -7,23 +7,23 @@ export interface CopyEl extends HTMLElement { copyValue: string; } -/** 文本复制指令(默认双击复制) */ +/** Chỉ thị sao chép văn bản (mặc định là sao chép khi double click) */ export const copy: Directive = { mounted(el: CopyEl, binding: DirectiveBinding) { const { value } = binding; if (value) { el.copyValue = value; const arg = binding.arg ?? "dblclick"; - // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + // Đăng ký sử dụng addEventListener khi mounted và tự động gỡ bỏ addEventListener khi unmounted useEventListener(el, arg, () => { const success = copyTextToClipboard(el.copyValue); success - ? message("复制成功", { type: "success" }) - : message("复制失败", { type: "error" }); + ? message("Sao chép thành công", { type: "success" }) + : message("Sao chép thất bại", { type: "error" }); }); } else { throw new Error( - '[Directive: copy]: need value! Like v-copy="modelValue"' + '[Directive: copy]: cần giá trị! Ví dụ: v-copy="modelValue"' ); } }, diff --git a/src/directives/optimize/index.ts b/src/directives/optimize/index.ts index 7b92538..5878a03 100644 --- a/src/directives/optimize/index.ts +++ b/src/directives/optimize/index.ts @@ -9,19 +9,19 @@ import { useEventListener } from "@vueuse/core"; import type { Directive, DirectiveBinding } from "vue"; export interface OptimizeOptions { - /** 事件名 */ + /** Tên sự kiện */ event: string; - /** 事件触发的方法 */ + /** Phương thức gọi sự kiện */ fn: (...params: any) => any; - /** 是否立即执行 */ + /** Có thực thi ngay lập tức hay không */ immediate?: boolean; - /** 防抖或节流的延迟时间(防抖默认:`200`毫秒、节流默认:`1000`毫秒) */ + /** Thời gian chậm hành động hoặc giảm tốc (giảm tốc mặc định: `200` mili giây, giảm tốc mặc định: `1000` mili giây) */ timeout?: number; - /** 传递的参数 */ + /** Tham số truyền vào */ params?: any; } -/** 防抖(v-optimize或v-optimize:debounce)、节流(v-optimize:throttle)指令 */ +/** Chỉ thị giảm tốc (v-optimize hoặc v-optimize:giảm tốc), giảm tốc (v-optimize:giảm tốc) */ export const optimize: Directive = { mounted(el: HTMLElement, binding: DirectiveBinding) { const { value } = binding; @@ -35,11 +35,11 @@ export const optimize: Directive = { params = isObject(params) ? Array.of(params) : params; } else { throw new Error( - "[Directive: optimize]: `params` must be an array or object" + "[Chỉ thị: optimize]: `params` phải là một mảng hoặc đối tượng" ); } } - // Register using addEventListener on mounted, and removeEventListener automatically on unmounted + // Đăng ký sử dụng addEventListener khi mounted và tự động gỡ bỏ addEventListener khi unmounted useEventListener( el, value.event, @@ -56,12 +56,12 @@ export const optimize: Directive = { ); } else { throw new Error( - "[Directive: optimize]: `event` and `fn` are required, and `fn` must be a function" + "[Chỉ thị: optimize]: `event` và `fn` là bắt buộc, và `fn` phải là một hàm" ); } } else { throw new Error( - "[Directive: optimize]: only `debounce` and `throttle` are supported" + "[Chỉ thị: optimize]: chỉ hỗ trợ `debounce` và `throttle`" ); } } diff --git a/src/layout/components/lay-content/index.vue b/src/layout/components/lay-content/index.vue index 5810d66..434a7b3 100644 --- a/src/layout/components/lay-content/index.vue +++ b/src/layout/components/lay-content/index.vue @@ -189,7 +189,7 @@ const transitionMain = defineComponent({ - + diff --git a/src/layout/components/lay-navbar/index.vue b/src/layout/components/lay-navbar/index.vue index e9030ed..47f5150 100644 --- a/src/layout/components/lay-navbar/index.vue +++ b/src/layout/components/lay-navbar/index.vue @@ -48,9 +48,9 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
- + - + - 简体中文 + Tiếng Việt - + - + - + @@ -168,7 +168,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang(); padding: 5px 40px; } - .check-zh { + .check-vi { position: absolute; left: 20px; } diff --git a/src/layout/components/lay-sidebar/NavHorizontal.vue b/src/layout/components/lay-sidebar/NavHorizontal.vue index eec5f65..d343219 100644 --- a/src/layout/components/lay-sidebar/NavHorizontal.vue +++ b/src/layout/components/lay-sidebar/NavHorizontal.vue @@ -65,9 +65,9 @@ nextTick(() => { />
- + - + { - + - + - + @@ -140,7 +140,7 @@ nextTick(() => { padding: 5px 40px; } - .check-zh { + .check-vi { position: absolute; left: 20px; } diff --git a/src/layout/components/lay-sidebar/NavMix.vue b/src/layout/components/lay-sidebar/NavMix.vue index fc50ede..dbd2972 100644 --- a/src/layout/components/lay-sidebar/NavMix.vue +++ b/src/layout/components/lay-sidebar/NavMix.vue @@ -38,7 +38,7 @@ const { function getDefaultActive(routePath) { const wholeMenus = usePermissionStoreHook().wholeMenus; - /** 当前路由的父级路径 */ + /** Current route's parent paths */ const parentRoutes = getParentPaths(routePath, wholeMenus)[0]; defaultActive.value = !isAllEmpty(route.meta?.activePath) ? route.meta.activePath @@ -99,9 +99,9 @@ watch(
- + - + - + - 简体中文 + Tiếng Việt - + - + - + @@ -174,7 +174,7 @@ watch( padding: 5px 40px; } - .check-zh { + .check-vi { position: absolute; left: 20px; } diff --git a/src/layout/hooks/useDataThemeChange.ts b/src/layout/hooks/useDataThemeChange.ts index 80db6dd..82b26a1 100644 --- a/src/layout/hooks/useDataThemeChange.ts +++ b/src/layout/hooks/useDataThemeChange.ts @@ -18,21 +18,21 @@ import { export function useDataThemeChange() { const { layoutTheme, layout } = useLayout(); const themeColors = ref>([ - /* 亮白色 */ + /* Màu sáng trắng */ { color: "#ffffff", themeColor: "light" }, - /* 道奇蓝 */ + /* Xanh Đậm */ { color: "#1b2a47", themeColor: "default" }, - /* 深紫罗兰色 */ + /* Tím Đậm */ { color: "#722ed1", themeColor: "saucePurple" }, - /* 深粉色 */ + /* Hồng Đậm */ { color: "#eb2f96", themeColor: "pink" }, - /* 猩红色 */ + /* Đỏ Lửa */ { color: "#f5222d", themeColor: "dusk" }, - /* 橙红色 */ + /* Cam Đỏ */ { color: "#fa541c", themeColor: "volcano" }, - /* 绿宝石 */ + /* Ngọc Lục Bảo */ { color: "#13c2c2", themeColor: "mingQing" }, - /* 酸橙绿 */ + /* Xanh Lục Giác */ { color: "#52c41a", themeColor: "auroraGreen" } ]); @@ -48,7 +48,7 @@ export function useDataThemeChange() { targetEl.className = flag ? `${className} ${clsName}` : className; } - /** 设置导航主题色 */ + /** Set màu chủ đề điều hướng */ function setLayoutThemeColor( theme = getConfig().Theme ?? "light", isClick = true @@ -57,7 +57,7 @@ export function useDataThemeChange() { toggleTheme({ scopeName: `layout-theme-${theme}` }); - // 如果非isClick,保留之前的themeColor + // Nếu không phải là isClick, giữ lại themeColor trước đó const storageThemeColor = $storage.layout.themeColor; $storage.layout = { layout: layout.value, @@ -84,7 +84,7 @@ export function useDataThemeChange() { ); } - /** 设置 `element-plus` 主题色 */ + /** Set màu chủ đề `element-plus` */ const setEpThemeColor = (color: string) => { useEpThemeStoreHook().setEpThemeColor(color); document.documentElement.style.setProperty("--el-color-primary", color); @@ -96,7 +96,7 @@ export function useDataThemeChange() { } }; - /** 浅色、深色整体风格切换 */ + /** Chuyển đổi kiểu chủ đề sáng, tối */ function dataThemeChange(overall?: string) { overallStyle.value = overall; if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) { @@ -115,7 +115,7 @@ export function useDataThemeChange() { } } - /** 清空缓存并返回登录页 */ + /** Xóa bộ nhớ cache và quay lại trang đăng nhập */ function onReset() { removeToken(); storageLocal().clear(); diff --git a/src/main.ts b/src/main.ts index d0f733c..f041468 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,37 +1,35 @@ -import App from "./App.vue"; -import router from "./router"; -import { setupStore } from "@/store"; -import { useI18n } from "@/plugins/i18n"; -import { getPlatformConfig } from "./config"; -import { MotionPlugin } from "@vueuse/motion"; -// import { useEcharts } from "@/plugins/echarts"; -import { createApp, type Directive } from "vue"; -import { useElementPlus } from "@/plugins/elementPlus"; -import { injectResponsiveStorage } from "@/utils/responsive"; +import App from "./App.vue"; // Nhập component App.vue chính của ứng dụng +import router from "./router"; // Nhập định tuyến của Vue Router +import { setupStore } from "@/store"; // Sử dụng hàm setupStore từ module store +import { useI18n } from "@/plugins/i18n"; // Sử dụng plugin i18n cho quản lý ngôn ngữ +import { getPlatformConfig } from "./config"; // Lấy cấu hình nền tảng từ module config +import { MotionPlugin } from "@vueuse/motion"; // Sử dụng plugin motion từ VueUse +import { useEcharts } from "@/plugins/echarts"; // Sử dụng plugin Echarts +import { createApp, type Directive } from "vue"; // Tạo ứng dụng Vue mới +import { useVxeTable } from "@/plugins/vxeTable"; // Sử dụng plugin VxeTable +import { useElementPlus } from "@/plugins/elementPlus"; // Sử dụng plugin Element Plus +import { injectResponsiveStorage } from "@/utils/responsive"; // Sử dụng hàm injectResponsiveStorage từ util responsive -import Table from "@pureadmin/table"; -// import PureDescriptions from "@pureadmin/descriptions"; +import Table from "@pureadmin/table"; // Nhập component Table từ thư viện @pureadmin/table +import PureDescriptions from "@pureadmin/descriptions"; // Nhập component PureDescriptions từ thư viện @pureadmin/descriptions -// 引入重置样式 +// Nhập các file style để reset, import index.scss và tailwind.css import "./style/reset.scss"; -// 导入公共样式 import "./style/index.scss"; -// 一定要在main.ts中导入tailwind.css,防止vite每次hmr都会请求src/style/index.scss整体css文件导致热更新慢的问题 import "./style/tailwind.css"; -import "element-plus/dist/index.css"; -// 导入字体图标 -import "./assets/iconfont/iconfont.js"; -import "./assets/iconfont/iconfont.css"; +import "element-plus/dist/index.css"; // Import CSS của Element Plus +import "./assets/iconfont/iconfont.js"; // Nhập icon font +import "./assets/iconfont/iconfont.css"; // Import CSS của icon font -const app = createApp(App); +const app = createApp(App); // Tạo một ứng dụng Vue mới với component App.vue -// 自定义指令 +// Định nghĩa các custom directive import * as directives from "@/directives"; Object.keys(directives).forEach(key => { app.directive(key, (directives as { [key: string]: Directive })[key]); }); -// 全局注册@iconify/vue图标库 +// Đăng ký toàn cục thư viện iconify/vue import { IconifyIconOffline, IconifyIconOnline, @@ -41,23 +39,29 @@ app.component("IconifyIconOffline", IconifyIconOffline); app.component("IconifyIconOnline", IconifyIconOnline); app.component("FontIcon", FontIcon); -// 全局注册按钮级别权限组件 +// Đăng ký toàn cục component cho quyền truy cập cấp nút import { Auth } from "@/components/ReAuth"; app.component("Auth", Auth); -// 全局注册vue-tippy +// Đăng ký toàn cục Vue Tippy import "tippy.js/dist/tippy.css"; import "tippy.js/themes/light.css"; import VueTippy from "vue-tippy"; app.use(VueTippy); +// Lấy cấu hình nền tảng và khởi tạo ứng dụng getPlatformConfig(app).then(async config => { - setupStore(app); - app.use(router); - await router.isReady(); - injectResponsiveStorage(app, config); - app.use(MotionPlugin).use(useI18n).use(useElementPlus).use(Table); - // .use(PureDescriptions) - // .use(useEcharts); - app.mount("#app"); + setupStore(app); // Thiết lập store Vuex + app.use(router); // Sử dụng router Vue + await router.isReady(); // Chờ router sẵn sàng + injectResponsiveStorage(app, config); // Inject cấu hình đáp ứng vào ứng dụng + app + .use(MotionPlugin) // Sử dụng MotionPlugin từ VueUse + .use(useI18n) // Sử dụng plugin i18n + .use(useElementPlus) // Sử dụng plugin Element Plus + .use(Table) // Sử dụng component Table + .use(useVxeTable) // Sử dụng plugin VxeTable + .use(PureDescriptions) // Sử dụng component PureDescriptions + .use(useEcharts); // Sử dụng plugin Echarts + app.mount("#app"); // Gắn ứng dụng vào phần tử có id là "app" }); diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 0e0ae1e..2ffc8b1 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -1,15 +1,15 @@ -// 多组件库的国际化和本地项目国际化兼容 +// Internationalization compatibility for multiple component libraries and local projects import { type I18n, createI18n } from "vue-i18n"; import type { App, WritableComputedRef } from "vue"; import { responsiveStorageNameSpace } from "@/config"; import { storageLocal, isObject } from "@pureadmin/utils"; -// element-plus国际化 +// Element Plus internationalization import enLocale from "element-plus/es/locale/lang/en"; -import zhLocale from "element-plus/es/locale/lang/zh-cn"; +import zhLocale from "element-plus/es/locale/lang/vi"; const siphonI18n = (function () { - // 仅初始化一次国际化配置 + // Initialize internationalization configuration only once let cache = Object.fromEntries( Object.entries( import.meta.glob("../../locales/*.y(a)?ml", { eager: true }) @@ -18,14 +18,14 @@ const siphonI18n = (function () { return [matched, value.default]; }) ); - return (prefix = "zh-CN") => { + return (prefix = "vi") => { return cache[prefix]; }; })(); export const localesConfigs = { - zh: { - ...siphonI18n("zh-CN"), + vi: { + ...siphonI18n("vi"), ...zhLocale }, en: { @@ -34,7 +34,7 @@ export const localesConfigs = { } }; -/** 获取对象中所有嵌套对象的key键,并将它们用点号分割组成字符串 */ +/** Get keys of all nested objects in an object and concatenate them with dots */ function getObjectKeys(obj) { const stack = []; const keys: Set = new Set(); @@ -58,9 +58,9 @@ function getObjectKeys(obj) { return keys; } -/** 将展开的key缓存 */ +/** Cache expanded keys */ const keysCache: Map> = new Map(); -const flatI18n = (prefix = "zh-CN") => { +const flatI18n = (prefix = "vi") => { let cache = keysCache.get(prefix); if (!cache) { cache = getObjectKeys(siphonI18n(prefix)); @@ -70,16 +70,16 @@ const flatI18n = (prefix = "zh-CN") => { }; /** - * 国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配) - * @param message message - * @returns 转化后的message + * Internationalization transformation utility function + * @param message message to transform + * @returns transformed message */ export function transformI18n(message: any = "") { if (!message) { return ""; } - // 处理存储动态路由的title,格式 {zh:"",en:""} + // Handle dynamic route titles stored in format {zh:"",en:""} if (typeof message === "object") { const locale: string | WritableComputedRef | any = i18n.global.locale; @@ -88,17 +88,17 @@ export function transformI18n(message: any = "") { const key = message.match(/(\S*)\./)?.input; - if (key && flatI18n("zh-CN").has(key)) { + if (key && flatI18n("vi").has(key)) { return i18n.global.t.call(i18n.global.locale, message); - } else if (!key && Object.hasOwn(siphonI18n("zh-CN"), message)) { - // 兼容非嵌套形式的国际化写法 + } else if (!key && Object.hasOwn(siphonI18n("vi"), message)) { + // Compatible with non-nested internationalization format return i18n.global.t.call(i18n.global.locale, message); } else { return message; } } -/** 此函数只是配合i18n Ally插件来进行国际化智能提示,并无实际意义(只对提示起作用),如果不需要国际化可删除 */ +/** This function is only for internationalization intelligent prompts with i18n Ally plugin (only affects prompts), can be removed if internationalization is not needed */ export const $t = (key: string) => key; export const i18n: I18n = createI18n({ @@ -106,7 +106,7 @@ export const i18n: I18n = createI18n({ locale: storageLocal().getItem( `${responsiveStorageNameSpace()}locale` - )?.locale ?? "zh", + )?.locale ?? "vi", fallbackLocale: "en", messages: localesConfigs }); diff --git a/src/plugins/vxeTable.ts b/src/plugins/vxeTable.ts new file mode 100644 index 0000000..8874406 --- /dev/null +++ b/src/plugins/vxeTable.ts @@ -0,0 +1,103 @@ +import "vxe-table/lib/style.css"; +// import "xe-utils"; +// import XEUtils from "xe-utils"; +import type { App } from "vue"; +//import { i18n } from "@/plugins/i18n"; +//import en from "vxe-table/lib/locale/lang/en-US"; + +import { + // 全局对象 + VXETable, + // 表格功能 + // Filter, + // Edit, + // Menu, + // Export, + // Keyboard, + // Validator, + Custom, + // 可选组件 + Icon, + Column, + Grid, + Pager, + Select, + // Colgroup, + // Tooltip, + // Toolbar, + // Form, + // FormItem, + // FormGather, + // Checkbox, + // CheckboxGroup, + // Radio, + // RadioGroup, + // RadioButton, + // Switch, + // Input, + // Optgroup, + // Option, + // Textarea, + // Button, + // Modal, + // List, + // Pulldown, + // 表格 + Table +} from "vxe-table"; + +// 全局默认参数 +VXETable.setConfig({ + // i18n: (key, args) => { + // return unref(i18n.global.locale) === "zh" + // ? XEUtils.toFormatString(XEUtils.get(zh, key), args) + // : XEUtils.toFormatString(XEUtils.get(en, key), args); + // }, + // translate(key) { + // const NAMESPACED = ["el.", "buttons."]; + // if (key && NAMESPACED.findIndex(v => key.includes(v)) !== -1) { + // return i18n.global.t.call(i18n.global.locale, key); + // } + // return key; + // } +}); + +export function useVxeTable(app: App) { + // 表格功能 + app + // .use(Filter) + // .use(Edit) + // .use(Menu) + // .use(Export) + // .use(Keyboard) + // .use(Validator) + .use(Custom) + // 可选组件 + .use(Icon) + .use(Column) + .use(Grid) + .use(Pager) + .use(Select) + // .use(Colgroup) + // .use(Tooltip) + // .use(Toolbar) + // .use(Form) + // .use(FormItem) + // .use(FormGather) + // .use(Checkbox) + // .use(CheckboxGroup) + // .use(Radio) + // .use(RadioGroup) + // .use(RadioButton) + // .use(Switch) + // .use(Input) + // .use(Optgroup) + // .use(Option) + // .use(Textarea) + // .use(Button) + // .use(Modal) + // .use(List) + // .use(Pulldown) + // 安装表格 + .use(Table); +} diff --git a/src/router/utils.ts b/src/router/utils.ts index 1f68d24..c0c4e7e 100644 --- a/src/router/utils.ts +++ b/src/router/utils.ts @@ -26,7 +26,9 @@ const IFrame = () => import("@/layout/frame.vue"); // https://cn.vitejs.dev/guide/features.html#glob-import const modulesRoutes = import.meta.glob("/src/views/**/*.{vue,tsx}"); -// 动态路由 +// Định nghĩa các hàm xử lý định tuyến động + +// Lấy danh sách định tuyến từ backend import { getAsyncRoutes } from "@/api/routes"; function handRank(routeInfo: any) { @@ -39,10 +41,10 @@ function handRank(routeInfo: any) { : false; } -/** 按照路由中meta下的rank等级升序来排序路由 */ +/** Sắp xếp định tuyến theo thứ tự tăng dần dựa trên meta.rank trong route */ function ascending(arr: any[]) { arr.forEach((v, index) => { - // 当rank不存在时,根据顺序自动创建,首页路由永远在第一位 + // Khi không có rank, tự động tạo theo thứ tự, đảm bảo trang chủ luôn là trang đầu tiên if (handRank(v)) v.meta.rank = index + 2; }); return arr.sort( @@ -52,7 +54,7 @@ function ascending(arr: any[]) { ); } -/** 过滤meta中showLink为false的菜单 */ +/** Lọc các menu có meta.showLink === false */ function filterTree(data: RouteComponent[]) { const newTree = cloneDeep(data).filter( (v: { meta: { showLink: boolean } }) => v.meta?.showLink !== false @@ -63,7 +65,7 @@ function filterTree(data: RouteComponent[]) { return newTree; } -/** 过滤children长度为0的的目录,当目录下没有菜单时,会过滤此目录,目录没有赋予roles权限,当目录下只要有一个菜单有显示权限,那么此目录就会显示 */ +/** Lọc các menu con có độ dài === 0, các mục không có menu sẽ bị loại bỏ */ function filterChildrenTree(data: RouteComponent[]) { const newTree = cloneDeep(data).filter((v: any) => v?.children?.length !== 0); newTree.forEach( @@ -72,7 +74,7 @@ function filterChildrenTree(data: RouteComponent[]) { return newTree; } -/** 判断两个数组彼此是否存在相同值 */ +/** Kiểm tra xem hai mảng có phần tử chung hay không */ function isOneOfArray(a: Array, b: Array) { return Array.isArray(a) && Array.isArray(b) ? intersection(a, b).length > 0 @@ -81,7 +83,7 @@ function isOneOfArray(a: Array, b: Array) { : true; } -/** 从localStorage里取出当前登录用户的角色roles,过滤无权限的菜单 */ +/** Lấy danh sách các vai trò roles của người dùng từ localStorage và lọc các menu không có quyền */ function filterNoPermissionTree(data: RouteComponent[]) { const currentRoles = storageLocal().getItem>(userKey)?.roles ?? []; @@ -94,31 +96,31 @@ function filterNoPermissionTree(data: RouteComponent[]) { return filterChildrenTree(newTree); } -/** 通过指定 `key` 获取父级路径集合,默认 `key` 为 `path` */ +/** Lấy danh sách đường dẫn cha dựa trên `key` đã chỉ định, mặc định `key` là `path` */ function getParentPaths(value: string, routes: RouteRecordRaw[], key = "path") { - // 深度遍历查找 + // Duyệt sâu tìm kiếm function dfs(routes: RouteRecordRaw[], value: string, parents: string[]) { for (let i = 0; i < routes.length; i++) { const item = routes[i]; - // 返回父级path + // Trả về danh sách path cha if (item[key] === value) return parents; - // children不存在或为空则不递归 + // Nếu không có children hoặc children rỗng thì không đệ quy if (!item.children || !item.children.length) continue; - // 往下查找时将当前path入栈 + // Khi tìm thấy path thì đưa path hiện tại vào stack parents.push(item.path); if (dfs(item.children, value, parents).length) return parents; - // 深度遍历查找未找到时当前path 出栈 + // Khi tìm kiếm sâu không tìm thấy, bỏ path hiện tại ra khỏi stack parents.pop(); } - // 未找到时返回空数组 + // Khi không tìm thấy thì trả về mảng rỗng return []; } return dfs(routes, value, []); } -/** 查找对应 `path` 的路由信息 */ +/** Tìm thông tin định tuyến theo `path` đã chỉ định */ function findRouteByPath(path: string, routes: RouteRecordRaw[]) { let res = routes.find((item: { path: string }) => item.path == path); if (res) { @@ -149,14 +151,14 @@ function addPathMatch() { } } -/** 处理动态路由(后端返回的路由) */ +/** Xử lý định tuyến động (các route từ backend) */ function handleAsyncRoutes(routeList) { if (routeList.length === 0) { usePermissionStoreHook().handleWholeMenus(routeList); } else { formatFlatteningRoutes(addAsyncRoutes(routeList)).map( (v: RouteRecordRaw) => { - // 防止重复添加路由 + // Tránh thêm định tuyến trùng lặp if ( router.options.routes[0].children.findIndex( value => value.path === v.path @@ -164,9 +166,9 @@ function handleAsyncRoutes(routeList) { ) { return; } else { - // 切记将路由push到routes后还需要使用addRoute,这样路由才能正常跳转 + // Lưu ý sau khi thêm định tuyến vào routes cần sử dụng addRoute để định tuyến mới có thể hoạt động bình thường router.options.routes[0].children.push(v); - // 最终路由进行升序 + // Sắp xếp các định tuyến cuối cùng ascending(router.options.routes[0].children); if (!router.hasRoute(v?.name)) router.addRoute(v); const flattenRouters: any = router @@ -189,10 +191,10 @@ function handleAsyncRoutes(routeList) { addPathMatch(); } -/** 初始化路由(`new Promise` 写法防止在异步请求中造成无限循环)*/ +/** Khởi tạo định tuyến (`new Promise` để tránh vòng lặp vô hạn trong yêu cầu không đồng bộ) */ function initRouter() { if (getConfig()?.CachingAsyncRoutes) { - // 开启动态路由缓存本地localStorage + // Bật cache định tuyến động vào localStorage const key = "async-routes"; const asyncRouteList = storageLocal().getItem(key) as any; if (asyncRouteList && asyncRouteList?.length > 0) { @@ -220,9 +222,9 @@ function initRouter() { } /** - * 将多级嵌套路由处理成一维数组 - * @param routesList 传入路由 - * @returns 返回处理后的一维路由 + * Chuyển đổi mảng định tuyến nhiều cấp thành mảng một cấp + * @param routesList Mảng định tuyến đầu vào + * @returns Trả về mảng định tuyến đã được xử lý */ function formatFlatteningRoutes(routesList: RouteRecordRaw[]) { if (routesList.length === 0) return routesList; @@ -238,10 +240,10 @@ function formatFlatteningRoutes(routesList: RouteRecordRaw[]) { } /** - * 一维数组处理成多级嵌套数组(三级及以上的路由全部拍成二级,keep-alive 只支持到二级缓存) + * Xử lý mảng định tuyến một cấp thành mảng nhiều cấp (Tất cả định tuyến từ cấp 3 trở lên đều được chuyển thành cấp 2, keep-alive chỉ hỗ trợ đến cấp 2) * https://github.com/pure-admin/vue-pure-admin/issues/67 - * @param routesList 处理后的一维路由菜单数组 - * @returns 返回将一维数组重新处理成规定路由的格式 + * @param routesList Mảng định tuyến menu đã được xử lý + * @returns Trả về mảng định tuyến được chuyển đổi lại thành định dạng quy định */ function formatTwoStageRoutes(routesList: RouteRecordRaw[]) { if (routesList.length === 0) return routesList; @@ -263,7 +265,7 @@ function formatTwoStageRoutes(routesList: RouteRecordRaw[]) { return newRoutesList; } -/** 处理缓存路由(添加、删除、刷新) */ +/** Xử lý định tuyến cache (Thêm, xóa, làm mới) */ function handleAliveRoute({ name }: ToRouteType, mode?: string) { switch (mode) { case "add": @@ -298,23 +300,23 @@ function handleAliveRoute({ name }: ToRouteType, mode?: string) { } } -/** 过滤后端传来的动态路由 重新生成规范路由 */ +/** Lọc các định tuyến động trả về từ backend, tạo lại định dạng chuẩn của định tuyến */ function addAsyncRoutes(arrRoutes: Array) { if (!arrRoutes || !arrRoutes.length) return; const modulesRoutesKeys = Object.keys(modulesRoutes); arrRoutes.forEach((v: RouteRecordRaw) => { - // 将backstage属性加入meta,标识此路由为后端返回路由 + // Thêm thuộc tính meta, đánh dấu đây là định tuyến trả về từ backend v.meta.backstage = true; - // 父级的redirect属性取值:如果子级存在且父级的redirect属性不存在,默认取第一个子级的path;如果子级存在且父级的redirect属性存在,取存在的redirect属性,会覆盖默认值 + // Nếu có children và parent không có redirect, mặc định lấy path của children đầu tiên; Nếu có children và parent có redirect, lấy redirect của parent, sẽ ghi đè giá trị mặc định if (v?.children && v.children.length && !v.redirect) v.redirect = v.children[0].path; - // 父级的name属性取值:如果子级存在且父级的name属性不存在,默认取第一个子级的name;如果子级存在且父级的name属性存在,取存在的name属性,会覆盖默认值(注意:测试中发现父级的name不能和子级name重复,如果重复会造成重定向无效(跳转404),所以这里给父级的name起名的时候后面会自动加上`Parent`,避免重复) + // Nếu có children và parent không có name, mặc định lấy name của children đầu tiên; Nếu có children và parent có name, lấy name của parent, sẽ ghi đè giá trị mặc định (Chú ý: Trong thử nghiệm, name của parent không được trùng với name của children, nếu trùng sẽ dẫn đến không có redirect (chuyển hướng 404), vì vậy ở đây sẽ tự động thêm `Parent` vào cuối name của parent để tránh trùng) if (v?.children && v.children.length && !v.name) v.name = (v.children[0].name as string) + "Parent"; if (v.meta?.frameSrc) { v.component = IFrame; } else { - // 对后端传component组件路径和不传做兼容(如果后端传component组件路径,那么path可以随便写,如果不传,component组件路径会跟path保持一致) + // Thích nghi với component và đường dẫn component không trả về từ backend (nếu backend trả về đường dẫn component, thì path có thể viết bất kỳ, nếu không trả về, đường dẫn component sẽ giống path) const index = v?.component ? modulesRoutesKeys.findIndex(ev => ev.includes(v.component as any)) : modulesRoutesKeys.findIndex(ev => ev.includes(v.path)); @@ -327,20 +329,20 @@ function addAsyncRoutes(arrRoutes: Array) { return arrRoutes; } -/** 获取路由历史模式 https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */ +/** Lấy chế độ lịch sử định tuyến https://next.router.vuejs.org/zh/guide/essentials/history-mode.html */ function getHistoryMode(routerHistory): RouterHistory { - // len为1 代表只有历史模式 为2 代表历史模式中存在base参数 https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1 + // Nếu len = 1 chỉ có lịch sử, len = 2 có tham số base trong lịch sử https://next.router.vuejs.org/zh/api/#%E5%8F%82%E6%95%B0-1 const historyMode = routerHistory.split(","); const leftMode = historyMode[0]; const rightMode = historyMode[1]; - // no param + // Không có tham số if (historyMode.length === 1) { if (leftMode === "hash") { return createWebHashHistory(""); } else if (leftMode === "h5") { return createWebHistory(""); } - } //has param + } // Có tham số else if (historyMode.length === 2) { if (leftMode === "hash") { return createWebHashHistory(rightMode); @@ -350,15 +352,15 @@ function getHistoryMode(routerHistory): RouterHistory { } } -/** 获取当前页面按钮级别的权限 */ +/** Lấy quyền của nút ở trang hiện tại */ function getAuths(): Array { return router.currentRoute.value.meta.auths as Array; } -/** 是否有按钮级别的权限 */ +/** Kiểm tra quyền của nút */ function hasAuth(value: string | Array): boolean { if (!value) return false; - /** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */ + /** Lấy tất cả các giá trị `code` tuỳ chỉnh từ trường `meta` của route hiện tại */ const metaAuths = getAuths(); if (!metaAuths) return false; const isAuths = isString(value) @@ -379,7 +381,7 @@ function handleTopMenu(route) { } } -/** 获取所有菜单中的第一个菜单(顶级菜单)*/ +/** Lấy menu đầu tiên (menu cấp đỉnh) từ tất cả các menu */ function getTopMenu(tag = false): menuType { const topMenu = handleTopMenu( usePermissionStoreHook().wholeMenus[0]?.children[0] diff --git a/src/store/modules/app.ts b/src/store/modules/app.ts index 2644aef..145ef00 100644 --- a/src/store/modules/app.ts +++ b/src/store/modules/app.ts @@ -19,13 +19,13 @@ export const useAppStore = defineStore({ withoutAnimation: false, isClickCollapse: false }, - // 这里的layout用于监听容器拖拉后恢复对应的导航模式 + // Layout này được sử dụng để lắng nghe khi container kéo thả và phục hồi chế độ điều hướng tương ứng layout: storageLocal().getItem( `${responsiveStorageNameSpace()}layout` )?.layout ?? getConfig().Layout, device: deviceDetection() ? "mobile" : "desktop", - // 浏览器窗口的可视区域大小 + // Kích thước vùng hiển thị của cửa sổ trình duyệt viewportSize: { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight diff --git a/src/style/reset.scss b/src/style/reset.scss index d79cdd9..ab45bf1 100644 --- a/src/style/reset.scss +++ b/src/style/reset.scss @@ -23,14 +23,15 @@ html { body { width: 100%; - height: 100%; - margin: 0; - font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", - "Microsoft YaHei", "微软雅黑", Arial, sans-serif; - line-height: inherit; - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - text-rendering: optimizelegibility; + width: 100%; + height: 100%; + margin: 0; + font-family: "Segoe UI", Arial, sans-serif; + line-height: inherit; + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + text-rendering: optimizelegibility; + font-feature-settings: "cv02", "cv03", "cv04", "cv11"; } hr { diff --git a/src/utils/auth.ts b/src/utils/auth.ts index 20ca8b3..2fb4114 100644 --- a/src/utils/auth.ts +++ b/src/utils/auth.ts @@ -2,60 +2,62 @@ import Cookies from "js-cookie"; import { storageLocal } from "@pureadmin/utils"; import { useUserStoreHook } from "@/store/modules/user"; +// Định nghĩa interface cho thông tin dữ liệu người dùng export interface DataInfo { - /** token */ + /** Token truy cập */ accessToken: string; - /** `accessToken`的过期时间(时间戳) */ + /** Thời gian hết hạn của accessToken (dưới dạng timestamp) */ expires: T; - /** 用于调用刷新accessToken的接口时所需的token */ + /** Token dùng để làm mới accessToken */ refreshToken: string; - /** 头像 */ + /** Ảnh đại diện */ avatar?: string; - /** 用户名 */ + /** Tên đăng nhập */ username?: string; - /** 昵称 */ + /** Biệt danh */ nickname?: string; - /** 当前登录用户的角色 */ + /** Các vai trò của người dùng hiện tại */ roles?: Array; } -export const userKey = "user-info"; -export const TokenKey = "authorized-token"; -/** - * 通过`multiple-tabs`是否在`cookie`中,判断用户是否已经登录系统, - * 从而支持多标签页打开已经登录的系统后无需再登录。 - * 浏览器完全关闭后`multiple-tabs`将自动从`cookie`中销毁, - * 再次打开浏览器需要重新登录系统 - * */ -export const multipleTabsKey = "multiple-tabs"; +// Khai báo các hằng số +export const userKey = "user-info"; // Key cho localStorage lưu thông tin người dùng +export const TokenKey = "authorized-token"; // Key cho cookie lưu token truy cập +export const multipleTabsKey = "multiple-tabs"; // Key cho cookie kiểm tra nhiều tab mở -/** 获取`token` */ +/** + * Hàm lấy thông tin token từ cookie hoặc localStorage + * Nếu không tìm thấy token trong cookie, thử lấy từ localStorage + */ export function getToken(): DataInfo { - // 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错 return Cookies.get(TokenKey) ? JSON.parse(Cookies.get(TokenKey)) : storageLocal().getItem(userKey); } /** - * @description 设置`token`以及一些必要信息并采用无感刷新`token`方案 - * 无感刷新:后端返回`accessToken`(访问接口使用的`token`)、`refreshToken`(用于调用刷新`accessToken`的接口时所需的`token`,`refreshToken`的过期时间(比如30天)应大于`accessToken`的过期时间(比如2小时))、`expires`(`accessToken`的过期时间) - * 将`accessToken`、`expires`、`refreshToken`这三条信息放在key值为authorized-token的cookie里(过期自动销毁) - * 将`avatar`、`username`、`nickname`、`roles`、`refreshToken`、`expires`这六条信息放在key值为`user-info`的localStorage里(利用`multipleTabsKey`当浏览器完全关闭后自动销毁) + * Hàm thiết lập thông tin token và thông tin người dùng + * Sử dụng phương thức làm mới token mà không cần đăng nhập lại + * Lưu accessToken, expires và refreshToken vào cookie với key là TokenKey + * Lưu avatar, username, nickname, roles, refreshToken và expires vào localStorage với key là userKey */ export function setToken(data: DataInfo) { let expires = 0; const { accessToken, refreshToken } = data; const { isRemembered, loginDay } = useUserStoreHook(); - expires = new Date(data.expires).getTime(); // 如果后端直接设置时间戳,将此处代码改为expires = data.expires,然后把上面的DataInfo改成DataInfo即可 + + // Xử lý giá trị của expires + expires = new Date(data.expires).getTime(); const cookieString = JSON.stringify({ accessToken, expires, refreshToken }); + // Thiết lập cookie TokenKey với các thông tin vừa xử lý expires > 0 ? Cookies.set(TokenKey, cookieString, { expires: (expires - Date.now()) / 86400000 }) : Cookies.set(TokenKey, cookieString); + // Thiết lập cookie multipleTabsKey để kiểm tra nhiều tab Cookies.set( multipleTabsKey, "true", @@ -66,6 +68,7 @@ export function setToken(data: DataInfo) { : {} ); + // Hàm để thiết lập thông tin người dùng vào localStorage và store Vuex function setUserKey({ avatar, username, nickname, roles }) { useUserStoreHook().SET_AVATAR(avatar); useUserStoreHook().SET_USERNAME(username); @@ -81,6 +84,7 @@ export function setToken(data: DataInfo) { }); } + // Kiểm tra và thiết lập thông tin người dùng if (data.username && data.roles) { const { username, roles } = data; setUserKey({ @@ -107,14 +111,18 @@ export function setToken(data: DataInfo) { } } -/** 删除`token`以及key值为`user-info`的localStorage信息 */ +/** + * Hàm xóa thông tin token và thông tin người dùng từ cookie và localStorage + */ export function removeToken() { - Cookies.remove(TokenKey); - Cookies.remove(multipleTabsKey); - storageLocal().removeItem(userKey); + Cookies.remove(TokenKey); // Xóa cookie TokenKey + Cookies.remove(multipleTabsKey); // Xóa cookie multipleTabsKey + storageLocal().removeItem(userKey); // Xóa localStorage userKey } -/** 格式化token(jwt格式) */ +/** + * Hàm định dạng token JWT + */ export const formatToken = (token: string): string => { return "Bearer " + token; }; diff --git a/src/utils/globalPolyfills.ts b/src/utils/globalPolyfills.ts index e9bc9a8..7ade3aa 100644 --- a/src/utils/globalPolyfills.ts +++ b/src/utils/globalPolyfills.ts @@ -1,5 +1,4 @@ -// 如果项目出现 `global is not defined` 报错,可能是您引入某个库的问题,比如 aws-sdk-js https://github.com/aws/aws-sdk-js -// 解决办法就是将该文件引入 src/main.ts 即可 import "@/utils/globalPolyfills"; +// Nếu biến `global` chưa được định nghĩa, gán `window` cho `global` if (typeof (window as any).global === "undefined") { (window as any).global = window; } diff --git a/src/utils/http/index.ts b/src/utils/http/index.ts index 19b5be2..31daf95 100644 --- a/src/utils/http/index.ts +++ b/src/utils/http/index.ts @@ -1,3 +1,4 @@ +// Axios instance and related configurations import Axios, { type AxiosInstance, type AxiosRequestConfig, @@ -14,16 +15,16 @@ import NProgress from "../progress"; import { getToken, formatToken } from "@/utils/auth"; import { useUserStoreHook } from "@/store/modules/user"; -// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1 +// For more configurations, please refer to: www.axios-js.com/zh-cn/docs/#axios-request-config-1 const defaultConfig: AxiosRequestConfig = { - // 请求超时时间 + // Request timeout timeout: 10000, headers: { Accept: "application/json, text/plain, */*", "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest" }, - // 数组格式参数序列化(https://github.com/axios/axios/issues/5142) + // Array format parameter serialization (https://github.com/axios/axios/issues/5142) paramsSerializer: { serialize: stringify as unknown as CustomParamsSerializer } @@ -35,19 +36,19 @@ class PureHttp { this.httpInterceptorsResponse(); } - /** `token`过期后,暂存待执行的请求 */ + /** Queue for storing requests while token is refreshing */ private static requests = []; - /** 防止重复刷新`token` */ + /** Prevents duplicate token refresh */ private static isRefreshing = false; - /** 初始化配置对象 */ + /** Initial configuration object */ private static initConfig: PureHttpRequestConfig = {}; - /** 保存当前`Axios`实例对象 */ + /** Stores the current Axios instance */ private static axiosInstance: AxiosInstance = Axios.create(defaultConfig); - /** 重连原始请求 */ + /** Retry the original request */ private static retryOriginalRequest(config: PureHttpRequestConfig) { return new Promise(resolve => { PureHttp.requests.push((token: string) => { @@ -57,13 +58,13 @@ class PureHttp { }); } - /** 请求拦截 */ + /** Request interceptor */ private httpInterceptorsRequest(): void { PureHttp.axiosInstance.interceptors.request.use( async (config: PureHttpRequestConfig): Promise => { - // 开启进度条动画 + // Start progress bar animation NProgress.start(); - // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调 + // Prioritize checking if post/get methods have a callback, otherwise execute initialization settings callback, etc. if (typeof config.beforeRequestCallback === "function") { config.beforeRequestCallback(config); return config; @@ -72,7 +73,7 @@ class PureHttp { PureHttp.initConfig.beforeRequestCallback(config); return config; } - /** 请求白名单,放置一些不需要`token`的接口(通过设置请求白名单,防止`token`过期后再请求造成的死循环问题) */ + /** Request whitelist, for endpoints that do not require token (prevent token expiration causing infinite loop issue by setting request whitelist) */ const whiteList = ["/refresh-token", "/login"]; return whiteList.some(url => config.url.endsWith(url)) ? config @@ -84,7 +85,7 @@ class PureHttp { if (expired) { if (!PureHttp.isRefreshing) { PureHttp.isRefreshing = true; - // token过期刷新 + // Refresh token when expired useUserStoreHook() .handRefreshToken({ refreshToken: data.refreshToken }) .then(res => { @@ -115,15 +116,15 @@ class PureHttp { ); } - /** 响应拦截 */ + /** Response interceptor */ private httpInterceptorsResponse(): void { const instance = PureHttp.axiosInstance; instance.interceptors.response.use( (response: PureHttpResponse) => { const $config = response.config; - // 关闭进度条动画 + // End progress bar animation NProgress.done(); - // 优先判断post/get等方法是否传入回调,否则执行初始化设置等回调 + // Prioritize checking if post/get methods have a callback, otherwise execute initialization settings callback, etc. if (typeof $config.beforeResponseCallback === "function") { $config.beforeResponseCallback(response); return response.data; @@ -137,15 +138,15 @@ class PureHttp { (error: PureHttpError) => { const $error = error; $error.isCancelRequest = Axios.isCancel($error); - // 关闭进度条动画 + // End progress bar animation NProgress.done(); - // 所有的响应异常 区分来源为取消请求/非取消请求 + // All response errors distinguish between canceled requests and non-canceled requests return Promise.reject($error); } ); } - /** 通用请求工具函数 */ + /** Common request utility function */ public request( method: RequestMethods, url: string, @@ -159,7 +160,7 @@ class PureHttp { ...axiosConfig } as PureHttpRequestConfig; - // 单独处理自定义请求/响应回调 + // Handle custom request/response callbacks separately return new Promise((resolve, reject) => { PureHttp.axiosInstance .request(config) @@ -172,7 +173,7 @@ class PureHttp { }); } - /** 单独抽离的`post`工具函数 */ + /** Separated 'post' utility function */ public post( url: string, params?: AxiosRequestConfig

, @@ -181,7 +182,7 @@ class PureHttp { return this.request("post", url, params, config); } - /** 单独抽离的`get`工具函数 */ + /** Separated 'get' utility function */ public get( url: string, params?: AxiosRequestConfig

, diff --git a/src/utils/localforage/index.ts b/src/utils/localforage/index.ts index 013545f..c4c1a9d 100644 --- a/src/utils/localforage/index.ts +++ b/src/utils/localforage/index.ts @@ -6,17 +6,17 @@ class StorageProxy implements ProxyStorage { constructor(storageModel) { this.storage = storageModel; this.storage.config({ - // 首选IndexedDB作为第一驱动,不支持IndexedDB会自动降级到localStorage(WebSQL被弃用,详情看https://developer.chrome.com/blog/deprecating-web-sql) + // Ưu tiên sử dụng IndexedDB làm driver chính, nếu không hỗ trợ IndexedDB sẽ tự động hạ cấp xuống localStorage (WebSQL đã bị loại bỏ, xem chi tiết tại https://developer.chrome.com/blog/deprecating-web-sql) driver: [this.storage.INDEXEDDB, this.storage.LOCALSTORAGE], name: "pure-admin" }); } /** - * @description 将对应键名的数据保存到离线仓库 - * @param k 键名 - * @param v 键值 - * @param m 缓存时间(单位`分`,默认`0`分钟,永久缓存) + * @description Lưu dữ liệu với tên khóa tương ứng vào kho lưu trữ ngoại tuyến + * @param k Tên khóa + * @param v Giá trị + * @param m Thời gian lưu trữ (đơn vị phút, mặc định là 0 phút, lưu trữ vĩnh viễn) */ public async setItem(k: string, v: T, m = 0): Promise { return new Promise((resolve, reject) => { @@ -35,8 +35,8 @@ class StorageProxy implements ProxyStorage { } /** - * @description 从离线仓库中获取对应键名的值 - * @param k 键名 + * @description Lấy giá trị tương ứng với tên khóa từ kho lưu trữ ngoại tuyến + * @param k Tên khóa */ public async getItem(k: string): Promise { return new Promise((resolve, reject) => { @@ -54,8 +54,8 @@ class StorageProxy implements ProxyStorage { } /** - * @description 从离线仓库中删除对应键名的值 - * @param k 键名 + * @description Xóa giá trị tương ứng với tên khóa từ kho lưu trữ ngoại tuyến + * @param k Tên khóa */ public async removeItem(k: string) { return new Promise((resolve, reject) => { @@ -71,7 +71,7 @@ class StorageProxy implements ProxyStorage { } /** - * @description 从离线仓库中删除所有的键名,重置数据库 + * @description Xóa tất cả các tên khóa từ kho lưu trữ ngoại tuyến, đặt lại cơ sở dữ liệu */ public async clear() { return new Promise((resolve, reject) => { @@ -87,7 +87,7 @@ class StorageProxy implements ProxyStorage { } /** - * @description 获取数据仓库中所有的key + * @description Lấy tất cả các khóa từ kho dữ liệu */ public async keys() { return new Promise((resolve, reject) => { @@ -104,6 +104,6 @@ class StorageProxy implements ProxyStorage { } /** - * 二次封装 [localforage](https://localforage.docschina.org/) 支持设置过期时间,提供完整的类型提示 + * Bao bọc lại [localforage](https://localforage.docschina.org/) hỗ trợ thiết lập thời gian hết hạn, cung cấp gợi ý kiểu dữ liệu đầy đủ */ export const localForage = () => new StorageProxy(forage); diff --git a/src/utils/message.ts b/src/utils/message.ts index 40898ac..66246b7 100644 --- a/src/utils/message.ts +++ b/src/utils/message.ts @@ -6,34 +6,34 @@ type messageStyle = "el" | "antd"; type messageTypes = "info" | "success" | "warning" | "error"; interface MessageParams { - /** 消息类型,可选 `info` 、`success` 、`warning` 、`error` ,默认 `info` */ + /** Loại thông báo, có thể là `info`, `success`, `warning`, `error`. Mặc định là `info` */ type?: messageTypes; - /** 自定义图标,该属性会覆盖 `type` 的图标 */ + /** Biểu tượng tùy chỉnh, thuộc tính này sẽ ghi đè biểu tượng của `type` */ icon?: any; - /** 是否将 `message` 属性作为 `HTML` 片段处理,默认 `false` */ + /** Có sử dụng `message` như là một đoạn mã HTML hay không, mặc định là `false` */ dangerouslyUseHTMLString?: boolean; - /** 消息风格,可选 `el` 、`antd` ,默认 `antd` */ + /** Kiểu giao diện của thông báo, có thể là `el` hoặc `antd`, mặc định là `antd` */ customClass?: messageStyle; - /** 显示时间,单位为毫秒。设为 `0` 则不会自动关闭,`element-plus` 默认是 `3000` ,平台改成默认 `2000` */ + /** Thời gian hiển thị, tính bằng mili giây. Đặt là `0` thì không tự động đóng, `element-plus` mặc định là `3000`, nền tảng đã đổi thành mặc định `2000` */ duration?: number; - /** 是否显示关闭按钮,默认值 `false` */ + /** Hiển thị nút đóng, mặc định là `false` */ showClose?: boolean; - /** 文字是否居中,默认值 `false` */ + /** Văn bản có căn giữa hay không, mặc định là `false` */ center?: boolean; - /** `Message` 距离窗口顶部的偏移量,默认 `20` */ + /** Độ lệch của `Message` so với đỉnh cửa sổ, mặc định là `20` */ offset?: number; - /** 设置组件的根元素,默认 `document.body` */ + /** Thiết lập phần tử gốc của thành phần, mặc định là `document.body` */ appendTo?: string | HTMLElement; - /** 合并内容相同的消息,不支持 `VNode` 类型的消息,默认值 `false` */ + /** Gộp các thông báo cùng nội dung giống nhau, không hỗ trợ loại `VNode`, mặc định là `false` */ grouping?: boolean; - /** 关闭时的回调函数, 参数为被关闭的 `message` 实例 */ + /** Callback khi đóng thông báo, tham số là instance `message` đã đóng */ onClose?: Function | null; } -/** 用法非常简单,参考 src/views/components/message/index.vue 文件 */ +/** Sử dụng rất đơn giản, xem tệp src/views/components/message/index.vue để biết thêm chi tiết */ /** - * `Message` 消息提示函数 + * Hàm thông báo `Message` */ const message = ( message: string | VNode | (() => VNode), @@ -70,7 +70,7 @@ const message = ( offset, appendTo, grouping, - // 全局搜 pure-message 即可知道该类的样式位置 + // Tìm kiếm toàn cầu pure-message để biết vị trí lớp này customClass: customClass === "antd" ? "pure-message" : "", onClose: () => (isFunction(onClose) ? onClose() : null) }); @@ -78,7 +78,7 @@ const message = ( }; /** - * 关闭所有 `Message` 消息提示函数 + * Đóng tất cả các thông báo `Message` */ const closeAllMessage = (): void => ElMessage.closeAll(); diff --git a/src/utils/mitt.ts b/src/utils/mitt.ts index 63816f1..7ca51b2 100644 --- a/src/utils/mitt.ts +++ b/src/utils/mitt.ts @@ -1,13 +1,20 @@ import type { Emitter } from "mitt"; import mitt from "mitt"; -/** 全局公共事件需要在此处添加类型 */ +/** Các sự kiện công cộng toàn cầu cần thêm các loại ở đây*/ type Events = { openPanel: string; tagViewsChange: string; tagViewsShowModel: string; logoChange: boolean; changLayoutRoute: string; + imageInfo: { + img: HTMLImageElement; + height: number; + width: number; + x: number; + y: number; + }; }; export const emitter: Emitter = mitt(); diff --git a/src/utils/preventDefault.ts b/src/utils/preventDefault.ts index 42da8df..3353408 100644 --- a/src/utils/preventDefault.ts +++ b/src/utils/preventDefault.ts @@ -1,25 +1,25 @@ import { useEventListener } from "@vueuse/core"; -/** 是否为`img`标签 */ +/** Kiểm tra xem có phải là thẻ `img` */ function isImgElement(element) { return typeof HTMLImageElement !== "undefined" ? element instanceof HTMLImageElement : element.tagName.toLowerCase() === "img"; } -// 在 src/main.ts 引入并调用即可 import { addPreventDefault } from "@/utils/preventDefault"; addPreventDefault(); +// Import và gọi từ src/main.ts import { addPreventDefault } from "@/utils/preventDefault"; addPreventDefault(); export const addPreventDefault = () => { - // 阻止通过键盘F12快捷键打开浏览器开发者工具面板 + // Ngăn chặn mở bảng công cụ phát triển của trình duyệt bằng phím tắt F12 useEventListener( window.document, "keydown", ev => ev.key === "F12" && ev.preventDefault() ); - // 阻止浏览器默认的右键菜单弹出(不会影响自定义右键事件) + // Ngăn chặn menu chuột phải mặc định của trình duyệt (không ảnh hưởng đến sự kiện chuột phải tùy chỉnh) useEventListener(window.document, "contextmenu", ev => ev.preventDefault()); - // 阻止页面元素选中 + // Ngăn chặn các phần tử trang được chọn useEventListener(window.document, "selectstart", ev => ev.preventDefault()); - // 浏览器中图片通常默认是可拖动的,并且可以在新标签页或窗口中打开,或者将其拖动到其他应用程序中,此处将其禁用,使其默认不可拖动 + // Trình duyệt thường cho phép kéo và thả hình ảnh mặc định và có thể mở trong tab hoặc cửa sổ mới, hoặc kéo nó vào ứng dụng khác. Ở đây, chúng tôi vô hiệu hóa nó để mặc định hình ảnh không thể kéo và thả useEventListener( window.document, "dragstart", diff --git a/src/utils/progress/index.ts b/src/utils/progress/index.ts index d309862..92fa02b 100644 --- a/src/utils/progress/index.ts +++ b/src/utils/progress/index.ts @@ -2,15 +2,15 @@ import NProgress from "nprogress"; import "nprogress/nprogress.css"; NProgress.configure({ - // 动画方式 + // Animation mode easing: "ease", - // 递增进度条的速度 + // Tăng tốc độ của thanh tiến trình speed: 500, - // 是否显示加载ico + // Có hiển thị ico đang tải hay không showSpinner: false, - // 自动递增间隔 + // khoảng thời gian tự động tăng trickleSpeed: 200, - // 初始化时的最小百分比 + // Tỷ lệ phần trăm tối thiểu khi khởi tạo minimum: 0.3 }); diff --git a/src/utils/propTypes.ts b/src/utils/propTypes.ts index a4d67ec..ab68d1a 100644 --- a/src/utils/propTypes.ts +++ b/src/utils/propTypes.ts @@ -22,7 +22,7 @@ const newPropTypes = createTypes({ integer: undefined }) as PropTypes; -// 从 vue-types v5.0 开始,extend()方法已经废弃,当前已改为官方推荐的ES6+方法 https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html#the-extend-method +// Bắt đầu từ vue-types v5.0, phương thức Extend() đã bị bỏ và được thay đổi thành phương thức ES6+ được đề xuất chính thức https://dwightjack.github.io/vue-types/advanced/extending-vue-types.html #phương thức -extend export default class propTypes extends newPropTypes { // a native-like validator that supports the `.validable` method static get style() { diff --git a/src/utils/responsive.ts b/src/utils/responsive.ts index 28f1bcf..2849f3b 100644 --- a/src/utils/responsive.ts +++ b/src/utils/responsive.ts @@ -1,4 +1,4 @@ -// 响应式storage +// Storage phản ứng import type { App } from "vue"; import Storage from "responsive-storage"; import { routerArrays } from "@/layout/types"; @@ -8,35 +8,35 @@ export const injectResponsiveStorage = (app: App, config: PlatformConfigs) => { const nameSpace = responsiveStorageNameSpace(); const configObj = Object.assign( { - // 国际化 默认中文zh + // Quốc tế hóa, mặc định là tiếng Việt vn locale: Storage.getData("locale", nameSpace) ?? { - locale: config.Locale ?? "zh" + locale: config.Locale ?? "vi" // Đặt mặc định là "vi" nếu không có dữ liệu }, - // layout模式以及主题 + // Mẫu bố trí và chủ đề layout: Storage.getData("layout", nameSpace) ?? { - layout: config.Layout ?? "vertical", - theme: config.Theme ?? "light", - darkMode: config.DarkMode ?? false, - sidebarStatus: config.SidebarStatus ?? true, - epThemeColor: config.EpThemeColor ?? "#409EFF", - themeColor: config.Theme ?? "light", // 主题色(对应系统配置中的主题色,与theme不同的是它不会受到浅色、深色整体风格切换的影响,只会在手动点击主题色时改变) - overallStyle: config.OverallStyle ?? "light" // 整体风格(浅色:light、深色:dark、自动:system) + layout: config.Layout ?? "vertical", // Bố cục mặc định là "vertical" nếu không có dữ liệu + theme: config.Theme ?? "light", // Chủ đề mặc định là "light" nếu không có dữ liệu + darkMode: config.DarkMode ?? false, // Chế độ tối mặc định là false nếu không có dữ liệu + sidebarStatus: config.SidebarStatus ?? true, // Trạng thái thanh bên mặc định là true nếu không có dữ liệu + epThemeColor: config.EpThemeColor ?? "#409EFF", // Màu chủ đề EP mặc định là "#409EFF" nếu không có dữ liệu + themeColor: config.Theme ?? "light", // Màu chủ đề (tương ứng với màu chủ đề trong cấu hình hệ thống, khác với chủ đề là nó sẽ không bị ảnh hưởng bởi việc chuyển đổi phong cách toàn cầu từ sáng sang tối, chỉ thay đổi khi người dùng nhấp vào màu chủ đề) + overallStyle: config.OverallStyle ?? "light" // Phong cách toàn cầu (Sáng: light, Tối: dark, Tự động: system) }, - // 系统配置-界面显示 + // Cấu hình hệ thống - Hiển thị giao diện configure: Storage.getData("configure", nameSpace) ?? { - grey: config.Grey ?? false, - weak: config.Weak ?? false, - hideTabs: config.HideTabs ?? false, - hideFooter: config.HideFooter ?? true, - showLogo: config.ShowLogo ?? true, - showModel: config.ShowModel ?? "smart", - multiTagsCache: config.MultiTagsCache ?? false, - stretch: config.Stretch ?? false + grey: config.Grey ?? false, // Xám mờ mặc định là false nếu không có dữ liệu + weak: config.Weak ?? false, // Yếu mặc định là false nếu không có dữ liệu + hideTabs: config.HideTabs ?? false, // Ẩn tab mặc định là false nếu không có dữ liệu + hideFooter: config.HideFooter ?? true, // Ẩn chân trang mặc định là true nếu không có dữ liệu + showLogo: config.ShowLogo ?? true, // Hiển thị logo mặc định là true nếu không có dữ liệu + showModel: config.ShowModel ?? "chrome", // Hiển thị mô hình mặc định là "chrome" nếu không có dữ liệu + multiTagsCache: config.MultiTagsCache ?? false, // Bộ nhớ cache nhiều thẻ mặc định là false nếu không có dữ liệu + stretch: config.Stretch ?? false // Kéo dài mặc định là false nếu không có dữ liệu } }, config.MultiTagsCache ? { - // 默认显示顶级菜单tag + // Hiển thị thẻ menu cấp độ cao nhất mặc định tags: Storage.getData("tags", nameSpace) ?? routerArrays } : {} diff --git a/src/utils/sso.ts b/src/utils/sso.ts index 18021d0..d2428eb 100644 --- a/src/utils/sso.ts +++ b/src/utils/sso.ts @@ -2,22 +2,22 @@ import { removeToken, setToken, type DataInfo } from "./auth"; import { subBefore, getQueryMap } from "@pureadmin/utils"; /** - * 简版前端单点登录,根据实际业务自行编写,平台启动后本地可以跳后面这个链接进行测试 http://localhost:8848/#/permission/page/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin - * 划重点: - * 判断是否为单点登录,不为则直接返回不再进行任何逻辑处理,下面是单点登录后的逻辑处理 - * 1.清空本地旧信息; - * 2.获取url中的重要参数信息,然后通过 setToken 保存在本地; - * 3.删除不需要显示在 url 的参数 - * 4.使用 window.location.replace 跳转正确页面 + * Đăng nhập đơn giản bằng SSO phía frontend, tuỳ chỉnh theo yêu cầu thực tế của doanh nghiệp, sau khi nền tảng khởi động, bạn có thể kiểm tra thử nghiệm địa phương bằng cách truy cập vào liên kết sau: http://localhost:8848/#/permission/page/index?username=sso&roles=admin&accessToken=eyJhbGciOiJIUzUxMiJ9.admin + * Chú ý: + * Kiểm tra xem có phải đăng nhập đơn giản không, nếu không phải thì trả về mà không thực hiện bất kỳ xử lý logic nào, dưới đây là xử lý logic sau khi đăng nhập đơn giản + * 1. Xóa thông tin cũ trên thiết bị địa phương; + * 2. Lấy thông tin tham số quan trọng từ URL, sau đó lưu trữ bằng setToken vào thiết bị địa phương; + * 3. Xóa các tham số không cần thiết để hiển thị trong URL; + * 4. Sử dụng window.location.replace để chuyển hướng đến trang chính xác. */ (function () { - // 获取 url 中的参数 + // Lấy các tham số từ URL const params = getQueryMap(location.href) as DataInfo; const must = ["username", "roles", "accessToken"]; const mustLength = must.length; if (Object.keys(params).length !== mustLength) return; - // url 参数满足 must 里的全部值,才判定为单点登录,避免非单点登录时刷新页面无限循环 + // Nếu các tham số URL đủ điều kiện trong must, đánh dấu là đăng nhập đơn giản, tránh vòng lặp vô hạn khi làm mới trang không phải đăng nhập đơn giản let sso = []; let start = 0; @@ -31,15 +31,15 @@ import { subBefore, getQueryMap } from "@pureadmin/utils"; } if (sso.length === mustLength) { - // 判定为单点登录 + // Đánh dấu là đăng nhập đơn giản - // 清空本地旧信息 + // Xóa thông tin cũ trên thiết bị client removeToken(); - // 保存新信息到本地 + // Lưu trữ thông tin mới vào thiết bị client setToken(params); - // 删除不需要显示在 url 的参数 + // Xóa các tham số không cần thiết để hiển thị trong URL delete params.roles; delete params.accessToken; @@ -51,7 +51,7 @@ import { subBefore, getQueryMap } from "@pureadmin/utils"; .replace(/:/g, "=") .replace(/,/g, "&")}`; - // 替换历史记录项 + // Thay thế mục lịch sử window.location.replace(newUrl); } else { return; diff --git a/src/utils/tree.ts b/src/utils/tree.ts index f8f3783..cee09fc 100644 --- a/src/utils/tree.ts +++ b/src/utils/tree.ts @@ -1,7 +1,7 @@ /** - * @description 提取菜单树中的每一项uniqueId - * @param tree 树 - * @returns 每一项uniqueId组成的数组 + * @description Trích xuất mỗi mục uniqueId từ cây menu + * @param tree Cây menu + * @returns Mảng chứa mỗi mục uniqueId */ export const extractPathList = (tree: any[]): any => { if (!Array.isArray(tree)) { @@ -21,10 +21,10 @@ export const extractPathList = (tree: any[]): any => { }; /** - * @description 如果父级下children的length为1,删除children并自动组建唯一uniqueId - * @param tree 树 - * @param pathList 每一项的id组成的数组 - * @returns 组件唯一uniqueId后的树 + * @description Nếu số lượng children của parent là 1, xóa children và tự động xây dựng uniqueId duy nhất + * @param tree Cây menu + * @param pathList Mảng chứa mỗi mục id + * @returns Cây với uniqueId duy nhất đã được xây dựng */ export const deleteChildren = (tree: any[], pathList = []): any => { if (!Array.isArray(tree)) { @@ -48,10 +48,10 @@ export const deleteChildren = (tree: any[], pathList = []): any => { }; /** - * @description 创建层级关系 - * @param tree 树 - * @param pathList 每一项的id组成的数组 - * @returns 创建层级关系后的树 + * @description Xây dựng cấu trúc cây + * @param tree Cây menu + * @param pathList Mảng chứa mỗi mục id + * @returns Cây sau khi đã xây dựng cấu trúc cây */ export const buildHierarchyTree = (tree: any[], pathList = []): any => { if (!Array.isArray(tree)) { @@ -72,10 +72,10 @@ export const buildHierarchyTree = (tree: any[], pathList = []): any => { }; /** - * @description 广度优先遍历,根据唯一uniqueId找当前节点信息 - * @param tree 树 - * @param uniqueId 唯一uniqueId - * @returns 当前节点信息 + * @description Duyệt cây theo chiều rộng, tìm thông tin của node hiện tại dựa trên uniqueId + * @param tree Cây menu + * @param uniqueId uniqueId + * @returns Thông tin của node hiện tại */ export const getNodeByUniqueId = ( tree: any[], @@ -96,11 +96,11 @@ export const getNodeByUniqueId = ( }; /** - * @description 向当前唯一uniqueId节点中追加字段 - * @param tree 树 - * @param uniqueId 唯一uniqueId - * @param fields 需要追加的字段 - * @returns 追加字段后的树 + * @description Thêm trường vào node hiện tại dựa trên uniqueId + * @param tree Cây menu + * @param uniqueId uniqueId + * @param fields Các trường cần thêm + * @returns Cây sau khi đã thêm trường */ export const appendFieldByUniqueId = ( tree: any[], @@ -127,12 +127,12 @@ export const appendFieldByUniqueId = ( }; /** - * @description 构造树型结构数据 - * @param data 数据源 - * @param id id字段 默认id - * @param parentId 父节点字段,默认parentId - * @param children 子节点字段,默认children - * @returns 追加字段后的树 + * @description Xây dựng cấu trúc dữ liệu cây + * @param data Dữ liệu nguồn + * @param id Trường id, mặc định là "id" + * @param parentId Trường parentId, mặc định là "parentId" + * @param children Trường children, mặc định là "children" + * @returns Cây sau khi đã xây dựng cấu trúc dữ liệu */ export const handleTree = ( data: any[], diff --git a/src/views/login/index.vue b/src/views/login/index.vue index 8df60f0..b087e13 100644 --- a/src/views/login/index.vue +++ b/src/views/login/index.vue @@ -53,7 +53,7 @@ const onLogin = async (formEl: FormInstance | undefined) => { .loginByUsername({ username: ruleForm.username, password: "admin123" }) .then(res => { if (res.success) { - // 获取后端路由 + // Fetch backend routes return initRouter().then(() => { router.push(getTopMenu(true).path).then(() => { message(t("login.pureLoginSuccess"), { type: "success" }); @@ -68,7 +68,7 @@ const onLogin = async (formEl: FormInstance | undefined) => { }); }; -/** 使用公共函数,避免`removeEventListener`失效 */ +/** Use common function to prevent `removeEventListener` failure */ function onkeypress({ code }: KeyboardEvent) { if (["Enter", "NumpadEnter"].includes(code)) { onLogin(ruleFormRef.value); @@ -88,7 +88,7 @@ onBeforeUnmount(() => {

- + { :inactive-icon="darkIcon" @change="dataThemeChange" /> - + {