feat:【ele】【mall】将 app-link-input 迁移到 mall/promotion/components 中,聚焦一点

This commit is contained in:
YunaiV
2025-10-25 15:53:51 +08:00
parent 457add90bd
commit 2909d1c4fa
17 changed files with 84 additions and 104 deletions

View File

@@ -16,7 +16,7 @@ import {
ElTooltip, ElTooltip,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';
import UploadFile from '#/components/upload/file-upload.vue'; import UploadFile from '#/components/upload/file-upload.vue';

View File

@@ -10,7 +10,7 @@ import {
ElTooltip, ElTooltip,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -11,7 +11,7 @@ import {
ElSwitch, ElSwitch,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';
import InputWithColor from '#/components/input-with-color/index.vue'; import InputWithColor from '#/components/input-with-color/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import type { ControlDot } from './controller'; import type { ControlDot } from './controller';
import type { AppLink } from '#/components/app-link-input/data'; import type { AppLink } from '#/views/mall/promotion/components/app-link-input/data';
import type { HotZoneItemProperty } from '#/components/diy-editor/components/mobile/HotZone/config'; import type { HotZoneItemProperty } from '#/components/diy-editor/components/mobile/HotZone/config';
import { ref } from 'vue'; import { ref } from 'vue';

View File

@@ -4,7 +4,7 @@ import type { ImageBarProperty } from './config';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { ElForm, ElFormItem } from 'element-plus'; import { ElForm, ElFormItem } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -6,7 +6,7 @@ import { ref } from 'vue';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { ElForm, ElFormItem, ElSlider, ElText } from 'element-plus'; import { ElForm, ElFormItem, ElSlider, ElText } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import MagicCubeEditor from '#/components/magic-cube-editor/index.vue'; import MagicCubeEditor from '#/components/magic-cube-editor/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -11,7 +11,7 @@ import {
ElSwitch, ElSwitch,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -4,7 +4,7 @@ import type { MenuListProperty } from './config';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { ElForm, ElFormItem, ElText } from 'element-plus'; import { ElForm, ElFormItem, ElText } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';
import InputWithColor from '#/components/input-with-color/index.vue'; import InputWithColor from '#/components/input-with-color/index.vue';

View File

@@ -13,7 +13,7 @@ import {
ElSwitch, ElSwitch,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ColorInput from '#/components/color-input/index.vue'; import ColorInput from '#/components/color-input/index.vue';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';

View File

@@ -15,7 +15,7 @@ import {
} from 'element-plus'; } from 'element-plus';
import appNavBarMp from '#/assets/imgs/diy/app-nav-bar-mp.png'; import appNavBarMp from '#/assets/imgs/diy/app-nav-bar-mp.png';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ColorInput from '#/components/color-input/index.vue'; import ColorInput from '#/components/color-input/index.vue';
import MagicCubeEditor from '#/components/magic-cube-editor/index.vue'; import MagicCubeEditor from '#/components/magic-cube-editor/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -4,7 +4,7 @@ import type { NoticeBarProperty } from './config';
import { useVModel } from '@vueuse/core'; import { useVModel } from '@vueuse/core';
import { ElCard, ElForm, ElFormItem, ElInput } from 'element-plus'; import { ElCard, ElForm, ElFormItem, ElInput } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ColorInput from '#/components/color-input/index.vue'; import ColorInput from '#/components/color-input/index.vue';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';

View File

@@ -15,7 +15,7 @@ import {
ElText, ElText,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ColorInput from '#/components/color-input/index.vue'; import ColorInput from '#/components/color-input/index.vue';
import Draggable from '#/components/draggable/index.vue'; import Draggable from '#/components/draggable/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -16,7 +16,7 @@ import {
ElTooltip, ElTooltip,
} from 'element-plus'; } from 'element-plus';
import AppLinkInput from '#/components/app-link-input/index.vue'; import { AppLinkInput } from '#/views/mall/promotion/components';
import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue'; import ComponentContainerProperty from '#/components/diy-editor/components/component-container-property.vue';
import InputWithColor from '#/components/input-with-color/index.vue'; import InputWithColor from '#/components/input-with-color/index.vue';
import UploadImg from '#/components/upload/image-upload.vue'; import UploadImg from '#/components/upload/image-upload.vue';

View File

@@ -13,17 +13,31 @@ import ProductCategorySelect from '#/views/mall/product/category/components/prod
import { APP_LINK_GROUP_LIST, APP_LINK_TYPE_ENUM } from './data'; import { APP_LINK_GROUP_LIST, APP_LINK_TYPE_ENUM } from './data';
// APP /** APP 链接选择弹框 */
defineOptions({ name: 'AppLinkSelectDialog' }); defineOptions({ name: 'AppLinkSelectDialog' });
//
const emit = defineEmits<{ const emit = defineEmits<{
appLinkChange: [appLink: AppLink]; appLinkChange: [appLink: AppLink];
change: [link: string]; change: [link: string];
}>(); }>();
//
const activeGroup = ref(APP_LINK_GROUP_LIST[0]?.name); const activeGroup = ref(APP_LINK_GROUP_LIST[0]?.name); //
// APP const activeAppLink = ref({} as AppLink); // APP
const activeAppLink = ref({} as AppLink);
const linkScrollbar = ref<ScrollbarInstance>(); //
const groupTitleRefs = ref<HTMLInputElement[]>([]); //
const groupScrollbar = ref<ScrollbarInstance>(); //
const groupBtnRefs = ref<ButtonInstance[]>([]); //
const detailSelectDialog = ref<{
id?: number;
type?: APP_LINK_TYPE_ENUM;
visible: boolean;
}>({
visible: false,
id: undefined,
type: undefined,
}); //
/** 打开弹窗 */ /** 打开弹窗 */
const dialogVisible = ref(false); const dialogVisible = ref(false);
@@ -47,7 +61,7 @@ const open = (link: string) => {
}; };
defineExpose({ open }); defineExpose({ open });
// APP /** 处理 APP 链接选中 */
const handleAppLinkSelected = (appLink: AppLink) => { const handleAppLinkSelected = (appLink: AppLink) => {
if (!isSameLink(appLink.path, activeAppLink.value.path)) { if (!isSameLink(appLink.path, activeAppLink.value.path)) {
activeAppLink.value = appLink; activeAppLink.value = appLink;
@@ -70,20 +84,18 @@ const handleAppLinkSelected = (appLink: AppLink) => {
} }
}; };
const handleSubmit = () => { function handleSubmit() {
dialogVisible.value = false; dialogVisible.value = false;
emit('change', activeAppLink.value.path); emit('change', activeAppLink.value.path);
emit('appLinkChange', activeAppLink.value); emit('appLinkChange', activeAppLink.value);
}; }
//
const groupTitleRefs = ref<HTMLInputElement[]>([]);
/** /**
* 处理右侧链接列表滚动 * 处理右侧链接列表滚动
* @param {object} param0 滚动事件参数 * @param {object} param0 滚动事件参数
* @param {number} param0.scrollTop 滚动条的位置 * @param {number} param0.scrollTop 滚动条的位置
*/ */
const handleScroll = ({ scrollTop }: { scrollTop: number }) => { function handleScroll({ scrollTop }: { scrollTop: number }) {
const titleEl = groupTitleRefs.value.find((titleEl: HTMLInputElement) => { const titleEl = groupTitleRefs.value.find((titleEl: HTMLInputElement) => {
// //
const { offsetHeight, offsetTop } = titleEl; const { offsetHeight, offsetTop } = titleEl;
@@ -96,12 +108,10 @@ const handleScroll = ({ scrollTop }: { scrollTop: number }) => {
// //
scrollToGroupBtn(activeGroup.value); scrollToGroupBtn(activeGroup.value);
} }
}; }
// /** 处理分组选中 */
const linkScrollbar = ref<ScrollbarInstance>(); function handleGroupSelected(group: string) {
//
const handleGroupSelected = (group: string) => {
activeGroup.value = group; activeGroup.value = group;
const titleRef = groupTitleRefs.value.find( const titleRef = groupTitleRefs.value.find(
(item: HTMLInputElement) => item.textContent === group, (item: HTMLInputElement) => item.textContent === group,
@@ -110,39 +120,26 @@ const handleGroupSelected = (group: string) => {
// //
linkScrollbar.value?.setScrollTop(titleRef.offsetTop); linkScrollbar.value?.setScrollTop(titleRef.offsetTop);
} }
}; }
// /** 自动滚动分组按钮,确保分组按钮保持在可视区域内 */
const groupScrollbar = ref<ScrollbarInstance>(); function scrollToGroupBtn(group: string) {
//
const groupBtnRefs = ref<ButtonInstance[]>([]);
//
const scrollToGroupBtn = (group: string) => {
const groupBtn = groupBtnRefs.value const groupBtn = groupBtnRefs.value
.map((btn: ButtonInstance) => btn.ref) .map((btn: ButtonInstance) => btn.ref)
.find((ref: HTMLButtonElement | undefined) => ref?.textContent === group); .find((ref: HTMLButtonElement | undefined) => ref?.textContent === group);
if (groupBtn) { if (groupBtn) {
groupScrollbar.value?.setScrollTop(groupBtn.offsetTop); groupScrollbar.value?.setScrollTop(groupBtn.offsetTop);
} }
}; }
// /** 是否为相同的链接(不比较参数,只比较链接) */
const isSameLink = (link1: string, link2: string) => { function isSameLink(link1: string, link2: string) {
return link2 ? link1.split('?')[0] === link2.split('?')[0] : false; return link2 ? link1.split('?')[0] === link2.split('?')[0] : false;
}; }
// /** 处理详情选择 */
const detailSelectDialog = ref<{ function handleProductCategorySelected(id: number) {
id?: number; // TODO @AI
type?: APP_LINK_TYPE_ENUM;
visible: boolean;
}>({
visible: false,
id: undefined,
type: undefined,
});
//
const handleProductCategorySelected = (id: number) => {
const url = new URL(activeAppLink.value.path, 'http://127.0.0.1'); const url = new URL(activeAppLink.value.path, 'http://127.0.0.1');
// id // id
url.searchParams.set('id', `${id}`); url.searchParams.set('id', `${id}`);
@@ -152,7 +149,7 @@ const handleProductCategorySelected = (id: number) => {
detailSelectDialog.value.visible = false; detailSelectDialog.value.visible = false;
// id // id
detailSelectDialog.value.id = undefined; detailSelectDialog.value.id = undefined;
}; }
</script> </script>
<template> <template>
<el-dialog v-model="dialogVisible" title="选择链接" width="65%"> <el-dialog v-model="dialogVisible" title="选择链接" width="65%">

View File

@@ -1,48 +1,32 @@
// APP 链接分组 /** APP 链接分组 */
export interface AppLinkGroup { export interface AppLinkGroup {
// 分组名称 name: string; // 分组名称
name: string; links: AppLink[]; // 链接列表
// 链接列表
links: AppLink[];
} }
// APP 链接 /** APP 链接 */
export interface AppLink { export interface AppLink {
// 链接名称 name: string; // 链接名称
name: string; path: string; // 链接地址
// 链接地址 type?: APP_LINK_TYPE_ENUM; // 链接的类型
path: string;
// 链接的类型
type?: APP_LINK_TYPE_ENUM;
} }
// APP 链接类型(需要特殊处理,例如商品详情) /** APP 链接类型(需要特殊处理,例如商品详情) */
export enum APP_LINK_TYPE_ENUM { export enum APP_LINK_TYPE_ENUM {
// 拼团活动 ACTIVITY_COMBINATION, // 拼团活动
ACTIVITY_COMBINATION, ACTIVITY_POINT, // 积分商城活动
// 积分商城活动 ACTIVITY_SECKILL, // 秒杀活动
ACTIVITY_POINT, ARTICLE_DETAIL, // 文章详情
// 秒杀活动 COUPON_DETAIL, // 优惠券详情
ACTIVITY_SECKILL, DIY_PAGE_DETAIL, // 自定义页面详情
// 文章详情 PRODUCT_CATEGORY_LIST, // 品类列表
ARTICLE_DETAIL, PRODUCT_DETAIL_COMBINATION, // 拼团商品详情
// 优惠券详情 PRODUCT_DETAIL_NORMAL, // 商品详情
COUPON_DETAIL, PRODUCT_DETAIL_SECKILL, // 秒杀商品详情
// 自定义页面详情 PRODUCT_LIST, // 商品列表
DIY_PAGE_DETAIL,
// 品类列表
PRODUCT_CATEGORY_LIST,
// 拼团商品详情
PRODUCT_DETAIL_COMBINATION,
// 商品详情
PRODUCT_DETAIL_NORMAL,
// 秒杀商品详情
PRODUCT_DETAIL_SECKILL,
// 商品列表
PRODUCT_LIST,
} }
// APP 链接列表(做一下持久化?) /** APP 链接列表(做一下持久化?) */
export const APP_LINK_GROUP_LIST = [ export const APP_LINK_GROUP_LIST = [
{ {
name: '商城', name: '商城',

View File

@@ -1,33 +1,29 @@
<script lang="ts" setup> <script lang="ts" setup>
// TODO @ diy-editor
import { ref, watch } from 'vue'; import { ref, watch } from 'vue';
import AppLinkSelectDialog from './app-link-select-dialog.vue'; import AppLinkSelectDialog from './app-link-select-dialog.vue';
// APP /** APP 链接输入框 */
defineOptions({ name: 'AppLinkInput' }); defineOptions({ name: 'AppLinkInput' });
// //
const props = defineProps({ const props = defineProps({
//
modelValue: { modelValue: {
type: String, type: String,
default: '', default: '',
}, }, //
}); });
// setter
const emit = defineEmits<{ const emit = defineEmits<{
'update:modelValue': [link: string]; 'update:modelValue': [link: string];
}>(); }>();
//
const appLink = ref('');
//
const dialogRef = ref();
//
const handleOpenDialog = () => dialogRef.value?.open(appLink.value);
// APP
const handleLinkSelected = (link: string) => (appLink.value = link);
// getter const dialogRef = ref(); //
const appLink = ref(''); //
const handleOpenDialog = () => dialogRef.value?.open(appLink.value); //
const handleLinkSelected = (link: string) => (appLink.value = link); // APP
watch( watch(
() => props.modelValue, () => props.modelValue,
() => (appLink.value = props.modelValue), () => (appLink.value = props.modelValue),
@@ -45,5 +41,6 @@ watch(
<el-button @click="handleOpenDialog">选择</el-button> <el-button @click="handleOpenDialog">选择</el-button>
</template> </template>
</el-input> </el-input>
<AppLinkSelectDialog ref="dialogRef" @change="handleLinkSelected" /> <AppLinkSelectDialog ref="dialogRef" @change="handleLinkSelected" />
</template> </template>

View File

@@ -0,0 +1,2 @@
export { default as VerticalButtonGroup } from './vertical-button-group/index.vue';
export { default as AppLinkInput } from './app-link-input/index.vue';