3 Commits
vite8 ... dev

Author SHA1 Message Date
YunaiV
1cbdf442ee feat: 添加 URL 验证工具函数并优化 area-select 组件的类型定义 2026-03-07 17:33:02 +08:00
YunaiV
f91a2702c9 merge: 合并 master 分支的 form-create 修复
合并 master 分支中关于 form-create 组件的修复,包括 area-select 和 iframe 组件的改进。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 11:32:54 +08:00
YunaiV
a8f67ab717 !335 fix: 修复上传头像时,如果图片加载失败,弹框一直loading的问题,针对 ele 版本 2026-03-07 11:21:03 +08:00
12 changed files with 49 additions and 38 deletions

View File

@@ -2,10 +2,11 @@
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue';
import { AreaLevelEnum } from '@vben/constants';
import { Cascader } from 'ant-design-vue';
import { getAreaTree } from '#/api/system/area';
import { AreaLevelEnum } from '@vben/constants';
defineOptions({ name: 'AreaSelect' });
@@ -40,7 +41,7 @@ interface AreaVO {
interface Props {
modelValue?: number[] | string[];
value?: number[] | string[];
level?: typeof AreaLevelEnum[keyof typeof AreaLevelEnum];
level?: (typeof AreaLevelEnum)[keyof typeof AreaLevelEnum];
disabled?: boolean;
placeholder?: string;
clearable?: boolean;

View File

@@ -72,9 +72,9 @@ const showPreview = computed(() => {
}
.iframe-preview {
overflow: hidden;
border: 1px solid #d9d9d9;
border-radius: 4px;
overflow: hidden;
}
.iframe-content {
@@ -87,8 +87,8 @@ const showPreview = computed(() => {
align-items: center;
justify-content: center;
min-height: 200px;
background-color: #fafafa;
border: 1px dashed #d9d9d9;
border-radius: 4px;
background-color: #fafafa;
}
</style>

View File

@@ -1,8 +1,9 @@
import { AreaLevelEnum } from '@vben/constants';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
import { AreaLevelEnum } from '@vben/constants';
/** 省市区选择器规则 */
export function useAreaSelectRule() {

View File

@@ -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,16 +30,3 @@ export const findIndex = <T = Recordable<any>>(
});
return index;
};
/**
* URL 验证
* @param path URL 路径
*/
export const isUrl = (path: string): boolean => {
// fix:修复hash路由无法跳转的问题
/* eslint-disable regexp/no-unused-capturing-group, regexp/no-super-linear-backtracking, regexp/no-useless-quantifier */
const reg =
/(((^https?:(?:\/\/)?)(?:[-:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%#/.\w-]*)?\??[-+=&%@.\w]*(?:#\w*)?)?)$/;
return reg.test(path);
/* eslint-enable regexp/no-unused-capturing-group, regexp/no-super-linear-backtracking, regexp/no-useless-quantifier */
};

View File

@@ -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"
/>
</div>

View File

@@ -144,6 +144,10 @@ function getRoundedCanvas() {
context.fill();
return canvas;
}
function handleImageError() {
emit('cropendError');
}
</script>
<template>
@@ -155,6 +159,7 @@ function getRoundedCanvas() {
:crossorigin="crossorigin"
:src="src"
:style="getImageStyle"
@error="handleImageError"
class="h-auto max-w-full"
/>
</div>

View File

@@ -2,10 +2,11 @@
<script lang="ts" setup>
import { onMounted, ref, watch } from 'vue';
import { AreaLevelEnum } from '@vben/constants';
import { ElCascader } from 'element-plus';
import { getAreaTree } from '#/api/system/area';
import { AreaLevelEnum } from '@vben/constants';
defineOptions({ name: 'AreaSelect' });
@@ -37,7 +38,7 @@ interface AreaVO {
// 接受父组件参数
interface Props {
modelValue?: number[] | string[];
level?: typeof AreaLevelEnum[keyof typeof AreaLevelEnum];
level?: (typeof AreaLevelEnum)[keyof typeof AreaLevelEnum];
disabled?: boolean;
placeholder?: string;
clearable?: boolean;

View File

@@ -68,9 +68,9 @@ const showPreview = computed(() => {
}
.iframe-preview {
overflow: hidden;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;
}
.iframe-content {
@@ -83,8 +83,8 @@ const showPreview = computed(() => {
align-items: center;
justify-content: center;
min-height: 200px;
background-color: #fafafa;
border: 1px dashed #dcdfe6;
border-radius: 4px;
background-color: #fafafa;
}
</style>

View File

@@ -1,8 +1,9 @@
import { AreaLevelEnum } from '@vben/constants';
import {
localeProps,
makeRequiredRule,
} from '#/components/form-create/helpers';
import { AreaLevelEnum } from '@vben/constants';
/** 省市区选择器规则 */
export function useAreaSelectRule() {

View File

@@ -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 查找的数组
@@ -28,16 +31,3 @@ export const findIndex = <T = Recordable<any>>(
});
return index;
};
/**
* URL 验证
* @param path URL 路径
*/
export const isUrl = (path: string): boolean => {
// fix:修复hash路由无法跳转的问题
/* eslint-disable regexp/no-unused-capturing-group, regexp/no-super-linear-backtracking, regexp/no-useless-quantifier */
const reg =
/(((^https?:(?:\/\/)?)(?:[-:&=+$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-:&=+$,\w]+@)[A-Za-z0-9.-]+)((?:\/[+~%#/.\w-]*)?\??[-+=&%@.\w]*(?:#\w*)?)?)$/;
return reg.test(path);
/* eslint-enable regexp/no-unused-capturing-group, regexp/no-super-linear-backtracking, regexp/no-useless-quantifier */
};

View File

@@ -18,6 +18,7 @@ export * from './tree';
export * from './unique';
export * from './update-css-variables';
export * from './upload';
export * from './url';
export * from './util';
export * from './uuid'; // add by 芋艿:从 vben2.0 复制
export * from './window';

View File

@@ -0,0 +1,12 @@
/**
* URL 验证
* @param path URL 路径
*/
export function isUrl(path: string): boolean {
try {
new URL(path);
return true;
} catch {
return false;
}
}