!333 fix(form-create): 【ele/antd】完成 vue3 review c153ff93 的所有 TODO 修复
Merge pull request !333 from puhui999/master-fixpull/336/MERGE
commit
c885c0c71a
|
|
@ -2,18 +2,17 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { Cascader } from 'ant-design-vue';
|
||||
|
||||
import { getAreaTree } from '#/api/system/area';
|
||||
import { AreaLevelEnum } from '@vben/constants';
|
||||
|
||||
defineOptions({ name: 'AreaSelect' });
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: undefined,
|
||||
value: undefined,
|
||||
level: 3,
|
||||
level: AreaLevelEnum.DISTRICT,
|
||||
disabled: false,
|
||||
placeholder: '请选择省市区',
|
||||
clearable: true,
|
||||
|
|
@ -41,7 +40,7 @@ interface AreaVO {
|
|||
interface Props {
|
||||
modelValue?: number[] | string[];
|
||||
value?: number[] | string[];
|
||||
level?: 1 | 2 | 3; // 1-省 2-市 3-区
|
||||
level?: typeof AreaLevelEnum[keyof typeof AreaLevelEnum];
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
clearable?: boolean;
|
||||
|
|
@ -69,7 +68,7 @@ async function loadAreaTree(): Promise<void> {
|
|||
try {
|
||||
loading.value = true;
|
||||
const data = await getAreaTree();
|
||||
|
||||
|
||||
// 根据 level 限制层级
|
||||
areaTree.value = filterTreeByLevel(data || [], props.level);
|
||||
} catch (error) {
|
||||
|
|
@ -83,10 +82,10 @@ async function loadAreaTree(): Promise<void> {
|
|||
// 根据层级过滤树形数据
|
||||
function filterTreeByLevel(tree: AreaVO[], maxLevel: number): AreaVO[] {
|
||||
if (maxLevel <= 0) return [];
|
||||
|
||||
|
||||
return tree.map((node) => {
|
||||
const newNode = { ...node };
|
||||
|
||||
|
||||
// 如果当前是最后一层,移除 children
|
||||
if (maxLevel === 1) {
|
||||
delete newNode.children;
|
||||
|
|
@ -94,7 +93,7 @@ function filterTreeByLevel(tree: AreaVO[], maxLevel: number): AreaVO[] {
|
|||
// 递归处理子节点
|
||||
newNode.children = filterTreeByLevel(node.children, maxLevel - 1);
|
||||
}
|
||||
|
||||
|
||||
return newNode;
|
||||
});
|
||||
}
|
||||
|
|
@ -106,7 +105,7 @@ function handleChange(value: number[] | undefined): void {
|
|||
emit('update:value', undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
emit('update:modelValue', value);
|
||||
emit('update:value', value);
|
||||
}
|
||||
|
|
@ -114,22 +113,22 @@ function handleChange(value: number[] | undefined): void {
|
|||
// 同步 modelValue 或 value 到内部选中值
|
||||
function syncSelectedValue(): void {
|
||||
const newValue = props.modelValue || props.value;
|
||||
|
||||
|
||||
if (newValue === undefined || newValue === null) {
|
||||
selectedValue.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 确保是数组格式
|
||||
if (Array.isArray(newValue)) {
|
||||
selectedValue.value = newValue as number[];
|
||||
} else {
|
||||
selectedValue.value = [newValue as number];
|
||||
}
|
||||
selectedValue.value = Array.isArray(newValue)
|
||||
? (newValue as number[])
|
||||
: [newValue as number];
|
||||
}
|
||||
|
||||
// 监听 modelValue 和 value 变化
|
||||
watch(() => props.modelValue || props.value, syncSelectedValue, { immediate: true });
|
||||
watch(() => props.modelValue || props.value, syncSelectedValue, {
|
||||
immediate: true,
|
||||
});
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(async () => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<!-- 网页 iframe 组件 (Ant Design Vue 版本) -->
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { isUrl } from '#/utils';
|
||||
|
||||
defineOptions({ name: 'IframeComponent' });
|
||||
|
||||
|
|
@ -16,11 +18,6 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
sandbox: '',
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
(e: 'update:value', value: string): void;
|
||||
}>();
|
||||
|
||||
// 接受父组件参数
|
||||
interface Props {
|
||||
modelValue?: string;
|
||||
|
|
@ -36,23 +33,14 @@ interface Props {
|
|||
}
|
||||
|
||||
// 显示的 URL(优先使用 url prop,其次使用 value 或 modelValue)
|
||||
const displayUrl = computed(() => props.url || props.value || props.modelValue || '');
|
||||
const displayUrl = computed(
|
||||
() => props.url || props.value || props.modelValue || '',
|
||||
);
|
||||
|
||||
// 是否显示预览
|
||||
const showPreview = computed(() => {
|
||||
return displayUrl.value && isValidUrl(displayUrl.value);
|
||||
return displayUrl.value && isUrl(displayUrl.value);
|
||||
});
|
||||
|
||||
// URL 验证
|
||||
function isValidUrl(url: string): boolean {
|
||||
if (!url || url.trim() === '') return false;
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
@ -104,4 +92,3 @@ function isValidUrl(url: string): boolean {
|
|||
background-color: #fafafa;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import {
|
||||
localeProps,
|
||||
makeRequiredRule,
|
||||
} from '#/components/form-create/helpers';
|
||||
import { AreaLevelEnum } from '@vben/constants';
|
||||
|
||||
/** 省市区选择器规则 */
|
||||
export function useAreaSelectRule() {
|
||||
|
|
@ -31,11 +30,11 @@ export function useAreaSelectRule() {
|
|||
type: 'select',
|
||||
field: 'level',
|
||||
title: '选择层级',
|
||||
value: 3,
|
||||
value: AreaLevelEnum.DISTRICT,
|
||||
options: [
|
||||
{ label: '省', value: 1 },
|
||||
{ label: '省/市', value: 2 },
|
||||
{ label: '省/市/区', value: 3 },
|
||||
{ label: '省', value: AreaLevelEnum.PROVINCE },
|
||||
{ label: '省/市', value: AreaLevelEnum.CITY },
|
||||
{ label: '省/市/区', value: AreaLevelEnum.DISTRICT },
|
||||
],
|
||||
info: '限制可选择的地区层级',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -27,3 +27,16 @@ 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 */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,17 +2,16 @@
|
|||
<script lang="ts" setup>
|
||||
import { onMounted, ref, watch } from 'vue';
|
||||
|
||||
import { handleTree } from '@vben/utils';
|
||||
|
||||
import { ElCascader } from 'element-plus';
|
||||
|
||||
import { getAreaTree } from '#/api/system/area';
|
||||
import { AreaLevelEnum } from '@vben/constants';
|
||||
|
||||
defineOptions({ name: 'AreaSelect' });
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
modelValue: undefined,
|
||||
level: 3,
|
||||
level: AreaLevelEnum.DISTRICT,
|
||||
disabled: false,
|
||||
placeholder: '请选择省市区',
|
||||
clearable: true,
|
||||
|
|
@ -38,7 +37,7 @@ interface AreaVO {
|
|||
// 接受父组件参数
|
||||
interface Props {
|
||||
modelValue?: number[] | string[];
|
||||
level?: 1 | 2 | 3; // 1-省 2-市 3-区
|
||||
level?: typeof AreaLevelEnum[keyof typeof AreaLevelEnum];
|
||||
disabled?: boolean;
|
||||
placeholder?: string;
|
||||
clearable?: boolean;
|
||||
|
|
@ -68,7 +67,7 @@ async function loadAreaTree(): Promise<void> {
|
|||
try {
|
||||
loading.value = true;
|
||||
const data = await getAreaTree();
|
||||
|
||||
|
||||
// 根据 level 限制层级
|
||||
areaTree.value = filterTreeByLevel(data || [], props.level);
|
||||
} catch (error) {
|
||||
|
|
@ -82,10 +81,10 @@ async function loadAreaTree(): Promise<void> {
|
|||
// 根据层级过滤树形数据
|
||||
function filterTreeByLevel(tree: AreaVO[], maxLevel: number): AreaVO[] {
|
||||
if (maxLevel <= 0) return [];
|
||||
|
||||
|
||||
return tree.map((node) => {
|
||||
const newNode = { ...node };
|
||||
|
||||
|
||||
// 如果当前是最后一层,移除 children
|
||||
if (maxLevel === 1) {
|
||||
delete newNode.children;
|
||||
|
|
@ -93,7 +92,7 @@ function filterTreeByLevel(tree: AreaVO[], maxLevel: number): AreaVO[] {
|
|||
// 递归处理子节点
|
||||
newNode.children = filterTreeByLevel(node.children, maxLevel - 1);
|
||||
}
|
||||
|
||||
|
||||
return newNode;
|
||||
});
|
||||
}
|
||||
|
|
@ -104,25 +103,23 @@ function handleChange(value: number[] | undefined): void {
|
|||
emit('update:modelValue', undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
emit('update:modelValue', value);
|
||||
}
|
||||
|
||||
// 同步 modelValue 到内部选中值
|
||||
function syncSelectedValue(): void {
|
||||
const newValue = props.modelValue;
|
||||
|
||||
|
||||
if (newValue === undefined || newValue === null) {
|
||||
selectedValue.value = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// 确保是数组格式
|
||||
if (Array.isArray(newValue)) {
|
||||
selectedValue.value = newValue as number[];
|
||||
} else {
|
||||
selectedValue.value = [newValue as number];
|
||||
}
|
||||
selectedValue.value = Array.isArray(newValue)
|
||||
? (newValue as number[])
|
||||
: [newValue as number];
|
||||
}
|
||||
|
||||
// 监听 modelValue 变化
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
<!-- 网页 iframe 组件 (Element Plus 版本) -->
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { isUrl } from '#/utils';
|
||||
|
||||
defineOptions({ name: 'IframeComponent' });
|
||||
|
||||
|
|
@ -15,10 +17,6 @@ const props = withDefaults(defineProps<Props>(), {
|
|||
sandbox: '',
|
||||
});
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'update:modelValue', value: string): void;
|
||||
}>();
|
||||
|
||||
// 接受父组件参数
|
||||
interface Props {
|
||||
modelValue?: string;
|
||||
|
|
@ -37,19 +35,8 @@ const displayUrl = computed(() => props.url || props.modelValue || '');
|
|||
|
||||
// 是否显示预览
|
||||
const showPreview = computed(() => {
|
||||
return displayUrl.value && isValidUrl(displayUrl.value);
|
||||
return displayUrl.value && isUrl(displayUrl.value);
|
||||
});
|
||||
|
||||
// URL 验证
|
||||
function isValidUrl(url: string): boolean {
|
||||
if (!url || url.trim() === '') return false;
|
||||
try {
|
||||
const urlObj = new URL(url);
|
||||
return urlObj.protocol === 'http:' || urlObj.protocol === 'https:';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { cloneDeep } from '@vben/utils';
|
||||
|
||||
import {
|
||||
localeProps,
|
||||
makeRequiredRule,
|
||||
} from '#/components/form-create/helpers';
|
||||
import { AreaLevelEnum } from '@vben/constants';
|
||||
|
||||
/** 省市区选择器规则 */
|
||||
export function useAreaSelectRule() {
|
||||
|
|
@ -31,11 +30,11 @@ export function useAreaSelectRule() {
|
|||
type: 'select',
|
||||
field: 'level',
|
||||
title: '选择层级',
|
||||
value: 3,
|
||||
value: AreaLevelEnum.DISTRICT,
|
||||
options: [
|
||||
{ label: '省', value: 1 },
|
||||
{ label: '省/市', value: 2 },
|
||||
{ label: '省/市/区', value: 3 },
|
||||
{ label: '省', value: AreaLevelEnum.PROVINCE },
|
||||
{ label: '省/市', value: AreaLevelEnum.CITY },
|
||||
{ label: '省/市/区', value: AreaLevelEnum.DISTRICT },
|
||||
],
|
||||
info: '限制可选择的地区层级',
|
||||
},
|
||||
|
|
|
|||
|
|
@ -28,3 +28,16 @@ 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 */
|
||||
};
|
||||
|
|
|
|||
|
|
@ -57,3 +57,12 @@ export const SystemUserSocialTypeEnum = {
|
|||
img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png',
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* 地区选择器层级枚举
|
||||
*/
|
||||
export const AreaLevelEnum = {
|
||||
PROVINCE: 1, // 省
|
||||
CITY: 2, // 市
|
||||
DISTRICT: 3, // 区
|
||||
} as const;
|
||||
|
|
|
|||
Loading…
Reference in New Issue