houzhongjian
2024-08-08 820397e43a0b64d35c6d31d2a55475061438593b
提交 | 用户 | 时间
820397 1 <template>
H 2   <Dialog v-model="dialogVisible" title="选择链接" width="65%">
3     <div class="h-500px flex gap-8px">
4       <!-- 左侧分组列表 -->
5       <el-scrollbar wrap-class="h-full" ref="groupScrollbar" view-class="flex flex-col">
6         <el-button
7           v-for="(group, groupIndex) in APP_LINK_GROUP_LIST"
8           :key="groupIndex"
9           :class="[
10             'm-r-16px m-l-0px! justify-start! w-90px',
11             { active: activeGroup === group.name }
12           ]"
13           ref="groupBtnRefs"
14           :text="activeGroup !== group.name"
15           :type="activeGroup === group.name ? 'primary' : 'default'"
16           @click="handleGroupSelected(group.name)"
17         >
18           {{ group.name }}
19         </el-button>
20       </el-scrollbar>
21       <!-- 右侧链接列表 -->
22       <el-scrollbar class="h-full flex-1" @scroll="handleScroll" ref="linkScrollbar">
23         <div v-for="(group, groupIndex) in APP_LINK_GROUP_LIST" :key="groupIndex">
24           <!-- 分组标题 -->
25           <div class="font-bold" ref="groupTitleRefs">{{ group.name }}</div>
26           <!-- 链接列表 -->
27           <el-tooltip
28             v-for="(appLink, appLinkIndex) in group.links"
29             :key="appLinkIndex"
30             :content="appLink.path"
31             placement="bottom"
32             :show-after="300"
33           >
34             <el-button
35               class="m-b-8px m-r-8px m-l-0px!"
36               :type="isSameLink(appLink.path, activeAppLink.path) ? 'primary' : 'default'"
37               @click="handleAppLinkSelected(appLink)"
38             >
39               {{ appLink.name }}
40             </el-button>
41           </el-tooltip>
42         </div>
43       </el-scrollbar>
44     </div>
45     <!-- 底部对话框操作按钮 -->
46     <template #footer>
47       <el-button type="primary" @click="handleSubmit">确 定</el-button>
48       <el-button @click="dialogVisible = false">取 消</el-button>
49     </template>
50   </Dialog>
51   <Dialog v-model="detailSelectDialog.visible" title="" width="50%">
52     <el-form class="min-h-200px">
53       <el-form-item
54         label="选择分类"
55         v-if="detailSelectDialog.type === APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST"
56       >
57         <ProductCategorySelect
58           v-model="detailSelectDialog.id"
59           :parent-id="0"
60           @update:model-value="handleProductCategorySelected"
61         />
62       </el-form-item>
63     </el-form>
64   </Dialog>
65 </template>
66 <script lang="ts" setup>
67 import { APP_LINK_GROUP_LIST, APP_LINK_TYPE_ENUM, AppLink } from './data'
68 import { ButtonInstance, ScrollbarInstance } from 'element-plus'
69 import { split } from 'lodash-es'
70 import ProductCategorySelect from '@/views/mall/product/category/components/ProductCategorySelect.vue'
71 import { getUrlNumberValue } from '@/utils'
72
73 // APP 链接选择弹框
74 defineOptions({ name: 'AppLinkSelectDialog' })
75 // 选中的分组,默认选中第一个
76 const activeGroup = ref(APP_LINK_GROUP_LIST[0].name)
77 // 选中的 APP 链接
78 const activeAppLink = ref({} as AppLink)
79
80 /** 打开弹窗 */
81 const dialogVisible = ref(false)
82 const open = (link: string) => {
83   activeAppLink.value.path = link
84   dialogVisible.value = true
85
86   // 滚动到当前的链接
87   const group = APP_LINK_GROUP_LIST.find((group) =>
88     group.links.some((linkItem) => {
89       const sameLink = isSameLink(linkItem.path, link)
90       if (sameLink) {
91         activeAppLink.value = { ...linkItem, path: link }
92       }
93       return sameLink
94     })
95   )
96   if (group) {
97     // 使用 nextTick 的原因:可能 Dom 还没生成,导致滚动失败
98     nextTick(() => handleGroupSelected(group.name))
99   }
100 }
101 defineExpose({ open })
102
103 // 处理 APP 链接选中
104 const handleAppLinkSelected = (appLink: AppLink) => {
105   if (!isSameLink(appLink.path, activeAppLink.value.path)) {
106     activeAppLink.value = appLink
107   }
108   switch (appLink.type) {
109     case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:
110       detailSelectDialog.value.visible = true
111       detailSelectDialog.value.type = appLink.type
112       // 返显
113       detailSelectDialog.value.id =
114         getUrlNumberValue('id', 'http://127.0.0.1' + activeAppLink.value.path) || undefined
115       break
116     default:
117       break
118   }
119 }
120
121 // 处理绑定值更新
122 const emit = defineEmits<{
123   change: [link: string]
124   appLinkChange: [appLink: AppLink]
125 }>()
126 const handleSubmit = () => {
127   dialogVisible.value = false
128   emit('change', activeAppLink.value.path)
129   emit('appLinkChange', activeAppLink.value)
130 }
131
132 // 分组标题引用列表
133 const groupTitleRefs = ref<HTMLInputElement[]>([])
134 /**
135  * 处理右侧链接列表滚动
136  * @param scrollTop 滚动条的位置
137  */
138 const handleScroll = ({ scrollTop }: { scrollTop: number }) => {
139   const titleEl = groupTitleRefs.value.find((titleEl: HTMLInputElement) => {
140     // 获取标题的位置信息
141     const { offsetHeight, offsetTop } = titleEl
142     // 判断标题是否在可视范围内
143     return scrollTop >= offsetTop && scrollTop < offsetTop + offsetHeight
144   })
145   // 只需处理一次
146   if (titleEl && activeGroup.value !== titleEl.textContent) {
147     activeGroup.value = titleEl.textContent || ''
148     // 同步左侧的滚动条位置
149     scrollToGroupBtn(activeGroup.value)
150   }
151 }
152
153 // 右侧滚动条
154 const linkScrollbar = ref<ScrollbarInstance>()
155 // 处理分组选中
156 const handleGroupSelected = (group: string) => {
157   activeGroup.value = group
158   const titleRef = groupTitleRefs.value.find((item: HTMLInputElement) => item.textContent === group)
159   if (titleRef) {
160     // 滚动分组标题
161     linkScrollbar.value?.setScrollTop(titleRef.offsetTop)
162   }
163 }
164
165 // 分组滚动条
166 const groupScrollbar = ref<ScrollbarInstance>()
167 // 分组引用列表
168 const groupBtnRefs = ref<ButtonInstance[]>([])
169 // 自动滚动分组按钮,确保分组按钮保持在可视区域内
170 const scrollToGroupBtn = (group: string) => {
171   const groupBtn = groupBtnRefs.value
172     .map((btn: ButtonInstance) => btn['ref'])
173     .find((ref: Node) => ref.textContent === group)
174   if (groupBtn) {
175     groupScrollbar.value?.setScrollTop(groupBtn.offsetTop)
176   }
177 }
178
179 // 是否为相同的链接(不比较参数,只比较链接)
180 const isSameLink = (link1: string, link2: string) => {
181   return split(link1, '?', 1)[0] === split(link2, '?', 1)[0]
182 }
183
184 // 详情选择对话框
185 const detailSelectDialog = ref<{
186   visible: boolean
187   id?: number
188   type?: APP_LINK_TYPE_ENUM
189 }>({
190   visible: false,
191   id: undefined,
192   type: undefined
193 })
194 // 处理详情选择
195 const handleProductCategorySelected = (id: number) => {
196   const url = new URL(activeAppLink.value.path, 'http://127.0.0.1')
197   // 修改 id 参数
198   url.searchParams.set('id', `${id}`)
199   // 排除域名
200   activeAppLink.value.path = `${url.pathname}${url.search}`
201   // 关闭对话框
202   detailSelectDialog.value.visible = false
203   // 重置 id
204   detailSelectDialog.value.id = undefined
205 }
206 </script>
207 <style lang="scss" scoped></style>