diff --git a/.gitee/image/common/iot-feature.png b/.gitee/image/common/iot-feature.png new file mode 100644 index 000000000..357781c53 Binary files /dev/null and b/.gitee/image/common/iot-feature.png differ diff --git a/.gitee/image/common/iot-preview.png b/.gitee/image/common/iot-preview.png new file mode 100644 index 000000000..58e8940eb Binary files /dev/null and b/.gitee/image/common/iot-preview.png differ diff --git a/.gitee/image/common/mes-feature.png b/.gitee/image/common/mes-feature.png new file mode 100644 index 000000000..33196fd06 Binary files /dev/null and b/.gitee/image/common/mes-feature.png differ diff --git a/.gitee/image/common/mes-preview.png b/.gitee/image/common/mes-preview.png new file mode 100644 index 000000000..a140979e7 Binary files /dev/null and b/.gitee/image/common/mes-preview.png differ diff --git a/.gitee/image/common/ruoyi-vue-pro-biz.png b/.gitee/image/common/ruoyi-vue-pro-biz.png index 24a385abe..41e313711 100644 Binary files a/.gitee/image/common/ruoyi-vue-pro-biz.png and b/.gitee/image/common/ruoyi-vue-pro-biz.png differ diff --git a/README.md b/README.md index b0d294bc2..3e8f10586 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,9 @@ ![功能分层](/.gitee/image/common/ruoyi-vue-pro-biz.png) -- 通用模块(必选):系统功能、基础设施 -- 通用模块(可选):工作流程、支付系统、数据报表、会员中心 -- 业务系统(按需):ERP 系统、CRM 系统、商城系统、微信公众号、AI 大模型 +* 通用模块(必选):系统功能、基础设施 +* 通用模块(可选):工作流程、支付系统、数据报表、会员中心 +* 业务系统(按需):ERP 系统、CRM 系统、MES 系统、商城系统、微信公众号、AI 大模型、IoT 物联网 ### 系统功能 @@ -219,6 +219,16 @@ ![功能图](/.gitee/image/common/mall-preview.png) +### 会员中心 + +| | 功能 | 描述 | +|-----|------|----------------------------------| +| 🚀 | 会员管理 | 会员是 C 端的消费者,该功能用于会员的搜索与管理 | +| 🚀 | 会员标签 | 对会员的标签进行创建、查询、修改、删除等操作 | +| 🚀 | 会员等级 | 对会员的等级、成长值进行管理,可用于订单折扣等会员权益 | +| 🚀 | 会员分组 | 对会员进行分组,用于用户画像、内容推送等运营手段 | +| 🚀 | 积分签到 | 回馈给签到、消费等行为的积分,会员可订单抵现、积分兑换等途径消耗 | + ### ERP 系统 演示地址: @@ -231,6 +241,14 @@ ![功能图](/.gitee/image/common/crm-feature.png) +### MES 系统 + +演示地址: + +![功能图](/.gitee/image/common/mes-feature.png) + +![功能图](/.gitee/image/common/mes-preview.png) + ### AI 大模型 演示地址: @@ -238,3 +256,11 @@ ![功能图](/.gitee/image/common/ai-feature.png) ![功能图](/.gitee/image/common/ai-preview.gif) + +### IoT 物联网 + +演示地址: + +![功能图](/.gitee/image/common/iot-feature.png) + +![预览图](/.gitee/image/common/iot-preview.png) diff --git a/apps/web-antd/src/utils/index.ts b/apps/web-antd/src/utils/index.ts index 5e797abcb..0a4c03ca5 100644 --- a/apps/web-antd/src/utils/index.ts +++ b/apps/web-antd/src/utils/index.ts @@ -3,6 +3,9 @@ import type { Recordable } from '@vben/types'; export * from './rangePickerProps'; export * from './routerHelper'; +// 从共享包导出 URL 工具函数 +export { isUrl } from '@vben/utils'; + /** * 查找数组对象的某个下标 * @param {Array} ary 查找的数组 @@ -27,14 +30,3 @@ export const findIndex = >( }); return index; }; - -/** - * URL 验证 - * @param path URL 路径 - */ -export const isUrl = (path: string): boolean => { - // fix:修复hash路由无法跳转的问题 - const reg = - /(((^https?:(?:\/\/)?)(?:[-:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%#/.\w-]*)?\??[-+=&%@.\w]*(?:#\w*)?)?)$/; - return reg.test(path); -}; diff --git a/apps/web-ele/src/components/cropper/cropper-modal.vue b/apps/web-ele/src/components/cropper/cropper-modal.vue index cf80965ad..5355f67fd 100644 --- a/apps/web-ele/src/components/cropper/cropper-modal.vue +++ b/apps/web-ele/src/components/cropper/cropper-modal.vue @@ -41,8 +41,8 @@ const [Modal, modalApi] = useVbenModal({ onConfirm: handleOk, onOpenChange(isOpen) { if (isOpen) { - // 打开时,进行 loading 加载。后续 CropperImage 组件加载完毕,会自动关闭 loading(通过 handleReady) - modalLoading(true); + // 只有存在可加载图片时才显示 loading,避免空图或异常链接导致一直 loading + modalLoading(!!src.value); } else { // 关闭时,清空右侧预览 previewSource.value = ''; @@ -65,10 +65,14 @@ function handleBeforeUpload(file: File) { reader.readAsDataURL(file); src.value = ''; previewSource.value = ''; + modalLoading(true); reader.addEventListener('load', (e) => { src.value = (e.target?.result as string) ?? ''; filename = file.name; }); + reader.addEventListener('error', () => { + modalLoading(false); + }); return false; } @@ -82,6 +86,10 @@ function handleReady(cropperInstance: CropperType) { modalLoading(false); } +function handleCropperError() { + modalLoading(false); +} + function handlerToolbar(event: string, arg?: number) { if (event === 'scaleX') { scaleX = arg = scaleX === -1 ? 1 : -1; @@ -133,6 +141,7 @@ async function handleOk() { :src="src" height="300px" @cropend="handleCropend" + @cropend-error="handleCropperError" @ready="handleReady" /> diff --git a/apps/web-ele/src/components/cropper/cropper.vue b/apps/web-ele/src/components/cropper/cropper.vue index d768fd87a..0db99c17b 100644 --- a/apps/web-ele/src/components/cropper/cropper.vue +++ b/apps/web-ele/src/components/cropper/cropper.vue @@ -144,6 +144,10 @@ function getRoundedCanvas() { context.fill(); return canvas; } + +function handleImageError() { + emit('cropendError'); +}