wui-components-v2 1.1.69 → 1.1.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/core/index.ts +74 -74
- package/api/feishu.ts +20 -20
- package/api/index.ts +4 -4
- package/api/login.ts +51 -51
- package/api/menu.ts +45 -45
- package/api/page.ts +114 -114
- package/api/sys.ts +12 -12
- package/api/tree.ts +28 -28
- package/components/action-popup/action-popup.vue +116 -116
- package/components/add-address-list/add-address-list.vue +187 -187
- package/components/add-address-page/add-address-page.vue +77 -77
- package/components/add-address-page/config.ts +297 -297
- package/components/audio-play/audio-play.vue +220 -220
- package/components/batch-upload-file/batch-upload-file.vue +75 -75
- package/components/card-botom-buttons/card-botom-buttons.vue +279 -279
- package/components/custom-date-picker/custom-date-picker.vue +106 -106
- package/components/custom-select-picker/custom-select-picker.vue +95 -95
- package/components/demo-block/demo-block.vue +63 -63
- package/components/demo-card/demo-card.vue +44 -44
- package/components/detail-popup/detail-popup.vue +99 -99
- package/components/evaluation-page/evaluation-page.vue +196 -196
- package/components/fold-card/fold-card.vue +171 -171
- package/components/form-control/form-control.vue +661 -661
- package/components/global-loading/GlobalLoading.md +366 -366
- package/components/global-loading/global-loading.vue +55 -55
- package/components/global-message/GlobalMessage.md +570 -570
- package/components/global-message/global-message.vue +68 -68
- package/components/global-toast/GlobalToast.md +261 -261
- package/components/global-toast/global-toast.vue +56 -56
- package/components/label-value/label-value.vue +144 -144
- package/components/label-value/open-value-more.vue +56 -56
- package/components/list-top-buttons/list-top-buttons.vue +19 -19
- package/components/loading/loading.vue +5 -5
- package/components/login-form/login-form.vue +126 -126
- package/components/login-form-box/login-form-box.vue +65 -65
- package/components/mulselect-picker/mulselect-picker.vue +86 -86
- package/components/phone-login-form/phone-login-form.vue +204 -204
- package/components/privacy-popup/privacy-popup.vue +191 -191
- package/components/product-card/product-card.vue +201 -201
- package/components/scan-input/scan-input.vue +406 -406
- package/components/search/search.vue +128 -128
- package/components/select-col-picker/select-col-picker.vue +168 -168
- package/components/tab-search/tab-search.vue +73 -73
- package/components/tree-select/components/TreeNode.vue +200 -200
- package/components/tree-select/components/index.vue +471 -471
- package/components/tree-select/components/tree-select.vue +56 -56
- package/components/tree-select/index.vue +93 -93
- package/components/user-choose/user-choose.vue +169 -151
- package/components/video-play/video-play.vue +56 -56
- package/components/wui-auto-update-component/wui-auto-update-component.vue +132 -132
- package/components/wui-default/wui-default.vue +49 -49
- package/components/wui-details-page/wui-details-page.vue +147 -147
- package/components/wui-edit-page/wui-edit-page-copy.vue +757 -757
- package/components/wui-edit-page/wui-edit-page.vue +793 -793
- package/components/wui-enume-select-control/wui-enume-select-control.vue +92 -92
- package/components/wui-list/wui-list.vue +235 -235
- package/components/wui-login/wui-login.vue +43 -43
- package/components/wui-login1/wui-login.vue +41 -41
- package/components/wui-menus/wui-menus-top.vue +137 -137
- package/components/wui-menus/wui-menus-top1.vue +8 -8
- package/components/wui-menus/wui-menus.vue +247 -247
- package/components/wui-menus1/components/banner-carousel.vue +320 -320
- package/components/wui-menus1/components/navbar.vue +43 -43
- package/components/wui-menus1/components/quick-panel.vue +55 -55
- package/components/wui-menus1/components/search-bar.vue +49 -49
- package/components/wui-menus1/components/section-menus.vue +208 -208
- package/components/wui-menus1/wui-menus-top.vue +718 -718
- package/components/wui-menus1/wui-menus-top1.vue +398 -398
- package/components/wui-menus1/wui-menus.vue +564 -564
- package/components/wui-notify-info/notify-handle.vue +53 -53
- package/components/wui-notify-info/wui-notify-info.vue +280 -280
- package/components/wui-scan-binding-sensor/wui-scan-binding-sensor.vue +75 -75
- package/components/wui-search-history-babbar/components/SearchBar.vue +70 -70
- package/components/wui-search-history-babbar/wui-search-history-babbar.vue +204 -204
- package/components/wui-select-list/wui-select-list.vue +310 -310
- package/components/wui-select-popup/wui-select-popup.vue +613 -612
- package/components/wui-system-settings/wui-system-settings.vue +144 -144
- package/components/wui-tabbar/wui-tabbar.vue +106 -106
- package/components/wui-tree-page/components/tree-item.vue +238 -238
- package/components/wui-tree-page/wui-tree-page.vue +297 -297
- package/components/wui-update-component/wui-update-component.vue +221 -221
- package/components/wui-user/wui-user.vue +202 -202
- package/composables/types/theme.ts +65 -65
- package/composables/types/user.ts +5 -5
- package/composables/useColorGenerator.ts +221 -221
- package/composables/useCompanyFieldFilter.ts +91 -91
- package/composables/useEnumes.ts +43 -43
- package/composables/useEventBus.ts +37 -37
- package/composables/useGlobalLoading.ts +47 -47
- package/composables/useGlobalMessage.ts +53 -53
- package/composables/useGlobalToast.ts +63 -63
- package/composables/useLocale.ts +54 -54
- package/composables/useManualTheme.ts +179 -179
- package/composables/useMenus.ts +193 -193
- package/composables/useSectionMenus.ts +120 -120
- package/composables/useTabbar.ts +50 -50
- package/composables/useTheme.ts +74 -74
- package/composables/useUser.ts +24 -24
- package/index.d.ts +90 -90
- package/index.ts +83 -83
- package/package.json +24 -24
- package/static/iconfont/iconfont.css +63 -63
- package/store/language.ts +151 -151
- package/store/manualThemeStore.ts +143 -143
- package/store/persist.ts +41 -41
- package/store/themeStore.ts +74 -74
- package/store/userStore.ts +22 -22
- package/styles/dark-mode.css +523 -523
- package/styles/dark-mode.min.css +1 -1
- package/styles/dark-mode.scss +498 -498
- package/type.ts +154 -154
- package/utils/control-tree.ts +353 -353
- package/utils/control-type-supportor.ts +148 -148
- package/utils/eventBus.ts +36 -36
- package/utils/idata-scan.ts +99 -99
- package/utils/index.ts +979 -979
- package/utils/rsaEncrypt.ts +10 -10
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
<script setup lang="ts">
|
|
2
|
-
import { computed, defineOptions, onMounted, ref } from 'vue'
|
|
3
|
-
import { useRouter } from 'uni-mini-router'
|
|
4
|
-
import { useMenus } from '../../composables/useMenus'
|
|
5
|
-
import type { MenuItem } from '../../composables/useMenus'
|
|
6
|
-
import { useGlobalToast } from '../../composables/useGlobalToast'
|
|
7
|
-
import { menu } from '../../api/menu'
|
|
8
|
-
import SearchHistory from './components/SearchBar.vue'
|
|
9
|
-
|
|
10
|
-
defineOptions({
|
|
11
|
-
name: 'WuiSearchHistoryBabbar',
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
const { gotoPage, filtermenu, menuList } = useMenus()
|
|
15
|
-
const router = useRouter()
|
|
16
|
-
const toast = useGlobalToast()
|
|
17
|
-
|
|
18
|
-
function queryList() {
|
|
19
|
-
// 此处请求仅为演示,请替换为自己项目中的请求
|
|
20
|
-
menu()
|
|
21
|
-
.then((res: any) => {
|
|
22
|
-
if (!res?.blocks?.length) {
|
|
23
|
-
toast.warning('暂无权限,请重新登录或联系管理员')
|
|
24
|
-
} else {
|
|
25
|
-
menuList.value = res?.blocks
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
.catch((res: any) => {
|
|
29
|
-
console.log(res)
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 提取树形结构中最深层的数据节点
|
|
35
|
-
* @param treeData - 树形结构数据
|
|
36
|
-
* @param childrenKey - 子节点的键名,默认为 'children'
|
|
37
|
-
* @returns 最深层节点组成的数组
|
|
38
|
-
*/
|
|
39
|
-
function extractDeepestNodes<T>(treeData: T[], childrenKey: string = 'children'): T[] {
|
|
40
|
-
const result: T[] = []
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* 递归遍历树节点
|
|
44
|
-
*/
|
|
45
|
-
function traverse(nodes: T[], depth: number = 0) {
|
|
46
|
-
for (const node of nodes) {
|
|
47
|
-
// 检查当前节点是否有子节点
|
|
48
|
-
const children = (node as any)[childrenKey]
|
|
49
|
-
|
|
50
|
-
if (children && Array.isArray(children) && children.length > 0) {
|
|
51
|
-
// 如果有子节点,继续深入
|
|
52
|
-
traverse(children, depth + 1)
|
|
53
|
-
} else {
|
|
54
|
-
// 如果没有子节点,这是最深层的节点,添加到结果中
|
|
55
|
-
result.push(node)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
traverse(treeData)
|
|
61
|
-
return result
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const a = computed(() => {
|
|
65
|
-
return extractDeepestNodes(filtermenu.value, 'items')
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
const searchQuery = ref('')
|
|
69
|
-
const searchHistory = ref<string[]>([])
|
|
70
|
-
const inputRef = ref<HTMLInputElement | null>(null)
|
|
71
|
-
|
|
72
|
-
const filteredMenus = computed<MenuItem[]>(() => {
|
|
73
|
-
if (!searchQuery.value.trim()) {
|
|
74
|
-
return a.value
|
|
75
|
-
}
|
|
76
|
-
const query = searchQuery.value.toLowerCase().trim()
|
|
77
|
-
return a.value.filter(
|
|
78
|
-
menu => menu.title.toLowerCase().includes(query) || (menu.tip && menu.tip.toLowerCase().includes(query))
|
|
79
|
-
)
|
|
80
|
-
})
|
|
81
|
-
|
|
82
|
-
function highlightedLabel(menu: MenuItem) {
|
|
83
|
-
if (!searchQuery.value.trim()) return menu.title
|
|
84
|
-
const query = searchQuery.value.toLowerCase().trim()
|
|
85
|
-
const index = menu.title.toLowerCase().indexOf(query)
|
|
86
|
-
if (index === -1) return menu.title
|
|
87
|
-
const before = menu.title.substring(0, index)
|
|
88
|
-
const match = menu.title.substring(index, index + query.length)
|
|
89
|
-
const after = menu.title.substring(index + query.length)
|
|
90
|
-
return `${before}<mark class="bg-yellow-200 text-yellow-800 rounded px-0.5">${match}</mark>${after}`
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// const handleBack = () => {
|
|
94
|
-
// router.back()
|
|
95
|
-
// }
|
|
96
|
-
|
|
97
|
-
function handleClear() {
|
|
98
|
-
searchQuery.value = ''
|
|
99
|
-
inputRef?.value?.focus()
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function handleMenuClick(menu: MenuItem) {
|
|
103
|
-
if (searchQuery.value.trim()) {
|
|
104
|
-
addToHistory(searchQuery.value.trim())
|
|
105
|
-
}
|
|
106
|
-
gotoPage(menu)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function addToHistory(keyword: string) {
|
|
110
|
-
if (!keyword.trim()) return
|
|
111
|
-
const current = [...searchHistory.value]
|
|
112
|
-
const index = current.indexOf(keyword)
|
|
113
|
-
if (index > -1) {
|
|
114
|
-
current.splice(index, 1)
|
|
115
|
-
}
|
|
116
|
-
current.unshift(keyword)
|
|
117
|
-
if (current.length > 5) {
|
|
118
|
-
current.pop()
|
|
119
|
-
}
|
|
120
|
-
searchHistory.value = current
|
|
121
|
-
localStorage.setItem('searchHistory', JSON.stringify(searchHistory.value))
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function handleSelectHistory(keyword: string) {
|
|
125
|
-
searchQuery.value = keyword
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
onMounted(() => {
|
|
129
|
-
queryList()
|
|
130
|
-
const saved = localStorage.getItem('searchHistory')
|
|
131
|
-
if (saved) {
|
|
132
|
-
try {
|
|
133
|
-
searchHistory.value = JSON.parse(saved)
|
|
134
|
-
} catch {
|
|
135
|
-
searchHistory.value = []
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
})
|
|
139
|
-
</script>
|
|
140
|
-
|
|
141
|
-
<template>
|
|
142
|
-
<view class="min-h-screen bg-slate-50">
|
|
143
|
-
<view class="sticky top-0 z-10 bg-white shadow-sm">
|
|
144
|
-
<view class="flex items-center gap-3 px-4 py-3">
|
|
145
|
-
<view class="h-11 flex flex-1 items-center rounded-xl bg-slate-100 px-4">
|
|
146
|
-
<input
|
|
147
|
-
ref="inputRef"
|
|
148
|
-
v-model="searchQuery"
|
|
149
|
-
type="text"
|
|
150
|
-
placeholder="搜索菜单、功能..."
|
|
151
|
-
class="ml-2 flex-1 bg-transparent text-base text-slate-800 outline-none placeholder:text-slate-400"
|
|
152
|
-
/>
|
|
153
|
-
<view
|
|
154
|
-
v-if="searchQuery"
|
|
155
|
-
class="cursor-pointer rounded-full p-1 transition-colors hover:bg-slate-200"
|
|
156
|
-
@click="handleClear"
|
|
157
|
-
>
|
|
158
|
-
<wd-icon name="close-circle-filled" size="22px" color="#ccc" />
|
|
159
|
-
</view>
|
|
160
|
-
</view>
|
|
161
|
-
</view>
|
|
162
|
-
</view>
|
|
163
|
-
|
|
164
|
-
<view class="px-4 pb-4">
|
|
165
|
-
<SearchHistory v-model="searchHistory" @select="handleSelectHistory" />
|
|
166
|
-
|
|
167
|
-
<view v-if="filteredMenus.length === 0" class="mt-6">
|
|
168
|
-
<p class="text-center text-sm text-slate-400">未找到相关菜单</p>
|
|
169
|
-
</view>
|
|
170
|
-
|
|
171
|
-
<view v-else class="mt-4">
|
|
172
|
-
<p class="mb-3 text-xs text-slate-400">
|
|
173
|
-
{{ searchQuery.trim() ? `找到 ${filteredMenus.length} 个相关菜单` : '全部菜单' }}
|
|
174
|
-
</p>
|
|
175
|
-
<view class="space-y-2">
|
|
176
|
-
<view
|
|
177
|
-
v-for="menu in filteredMenus"
|
|
178
|
-
:key="menu.id"
|
|
179
|
-
class="flex cursor-pointer items-center gap-3 rounded-xl bg-white p-3 shadow-sm transition-all duration-200 active:scale-[0.98] hover:shadow-md"
|
|
180
|
-
@click="handleMenuClick(menu)"
|
|
181
|
-
>
|
|
182
|
-
<view
|
|
183
|
-
:style="{ background: menu.color }"
|
|
184
|
-
class="h-10 w-10 flex flex-shrink-0 items-center justify-center rounded-lg"
|
|
185
|
-
>
|
|
186
|
-
<!-- <component
|
|
187
|
-
v-if="iconMap[menu.icon]"
|
|
188
|
-
:is="iconMap[menu.icon]"
|
|
189
|
-
class="w-5 h-5 text-white"
|
|
190
|
-
/> -->
|
|
191
|
-
<wd-icon name="picture" size="22px" color="#FFF" />
|
|
192
|
-
</view>
|
|
193
|
-
<view class="flex-1 text-left">
|
|
194
|
-
<p class="text-base text-slate-800 font-medium" v-html="highlightedLabel(menu)" />
|
|
195
|
-
<p v-if="menu.tip" class="mt-0.5 text-xs text-slate-400">
|
|
196
|
-
{{ menu.tip }}
|
|
197
|
-
</p>
|
|
198
|
-
</view>
|
|
199
|
-
</view>
|
|
200
|
-
</view>
|
|
201
|
-
</view>
|
|
202
|
-
</view>
|
|
203
|
-
</view>
|
|
204
|
-
</template>
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, defineOptions, onMounted, ref } from 'vue'
|
|
3
|
+
import { useRouter } from 'uni-mini-router'
|
|
4
|
+
import { useMenus } from '../../composables/useMenus'
|
|
5
|
+
import type { MenuItem } from '../../composables/useMenus'
|
|
6
|
+
import { useGlobalToast } from '../../composables/useGlobalToast'
|
|
7
|
+
import { menu } from '../../api/menu'
|
|
8
|
+
import SearchHistory from './components/SearchBar.vue'
|
|
9
|
+
|
|
10
|
+
defineOptions({
|
|
11
|
+
name: 'WuiSearchHistoryBabbar',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const { gotoPage, filtermenu, menuList } = useMenus()
|
|
15
|
+
const router = useRouter()
|
|
16
|
+
const toast = useGlobalToast()
|
|
17
|
+
|
|
18
|
+
function queryList() {
|
|
19
|
+
// 此处请求仅为演示,请替换为自己项目中的请求
|
|
20
|
+
menu()
|
|
21
|
+
.then((res: any) => {
|
|
22
|
+
if (!res?.blocks?.length) {
|
|
23
|
+
toast.warning('暂无权限,请重新登录或联系管理员')
|
|
24
|
+
} else {
|
|
25
|
+
menuList.value = res?.blocks
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.catch((res: any) => {
|
|
29
|
+
console.log(res)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 提取树形结构中最深层的数据节点
|
|
35
|
+
* @param treeData - 树形结构数据
|
|
36
|
+
* @param childrenKey - 子节点的键名,默认为 'children'
|
|
37
|
+
* @returns 最深层节点组成的数组
|
|
38
|
+
*/
|
|
39
|
+
function extractDeepestNodes<T>(treeData: T[], childrenKey: string = 'children'): T[] {
|
|
40
|
+
const result: T[] = []
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 递归遍历树节点
|
|
44
|
+
*/
|
|
45
|
+
function traverse(nodes: T[], depth: number = 0) {
|
|
46
|
+
for (const node of nodes) {
|
|
47
|
+
// 检查当前节点是否有子节点
|
|
48
|
+
const children = (node as any)[childrenKey]
|
|
49
|
+
|
|
50
|
+
if (children && Array.isArray(children) && children.length > 0) {
|
|
51
|
+
// 如果有子节点,继续深入
|
|
52
|
+
traverse(children, depth + 1)
|
|
53
|
+
} else {
|
|
54
|
+
// 如果没有子节点,这是最深层的节点,添加到结果中
|
|
55
|
+
result.push(node)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
traverse(treeData)
|
|
61
|
+
return result
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const a = computed(() => {
|
|
65
|
+
return extractDeepestNodes(filtermenu.value, 'items')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
const searchQuery = ref('')
|
|
69
|
+
const searchHistory = ref<string[]>([])
|
|
70
|
+
const inputRef = ref<HTMLInputElement | null>(null)
|
|
71
|
+
|
|
72
|
+
const filteredMenus = computed<MenuItem[]>(() => {
|
|
73
|
+
if (!searchQuery.value.trim()) {
|
|
74
|
+
return a.value
|
|
75
|
+
}
|
|
76
|
+
const query = searchQuery.value.toLowerCase().trim()
|
|
77
|
+
return a.value.filter(
|
|
78
|
+
menu => menu.title.toLowerCase().includes(query) || (menu.tip && menu.tip.toLowerCase().includes(query))
|
|
79
|
+
)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
function highlightedLabel(menu: MenuItem) {
|
|
83
|
+
if (!searchQuery.value.trim()) return menu.title
|
|
84
|
+
const query = searchQuery.value.toLowerCase().trim()
|
|
85
|
+
const index = menu.title.toLowerCase().indexOf(query)
|
|
86
|
+
if (index === -1) return menu.title
|
|
87
|
+
const before = menu.title.substring(0, index)
|
|
88
|
+
const match = menu.title.substring(index, index + query.length)
|
|
89
|
+
const after = menu.title.substring(index + query.length)
|
|
90
|
+
return `${before}<mark class="bg-yellow-200 text-yellow-800 rounded px-0.5">${match}</mark>${after}`
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// const handleBack = () => {
|
|
94
|
+
// router.back()
|
|
95
|
+
// }
|
|
96
|
+
|
|
97
|
+
function handleClear() {
|
|
98
|
+
searchQuery.value = ''
|
|
99
|
+
inputRef?.value?.focus()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function handleMenuClick(menu: MenuItem) {
|
|
103
|
+
if (searchQuery.value.trim()) {
|
|
104
|
+
addToHistory(searchQuery.value.trim())
|
|
105
|
+
}
|
|
106
|
+
gotoPage(menu)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function addToHistory(keyword: string) {
|
|
110
|
+
if (!keyword.trim()) return
|
|
111
|
+
const current = [...searchHistory.value]
|
|
112
|
+
const index = current.indexOf(keyword)
|
|
113
|
+
if (index > -1) {
|
|
114
|
+
current.splice(index, 1)
|
|
115
|
+
}
|
|
116
|
+
current.unshift(keyword)
|
|
117
|
+
if (current.length > 5) {
|
|
118
|
+
current.pop()
|
|
119
|
+
}
|
|
120
|
+
searchHistory.value = current
|
|
121
|
+
localStorage.setItem('searchHistory', JSON.stringify(searchHistory.value))
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function handleSelectHistory(keyword: string) {
|
|
125
|
+
searchQuery.value = keyword
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
onMounted(() => {
|
|
129
|
+
queryList()
|
|
130
|
+
const saved = localStorage.getItem('searchHistory')
|
|
131
|
+
if (saved) {
|
|
132
|
+
try {
|
|
133
|
+
searchHistory.value = JSON.parse(saved)
|
|
134
|
+
} catch {
|
|
135
|
+
searchHistory.value = []
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
</script>
|
|
140
|
+
|
|
141
|
+
<template>
|
|
142
|
+
<view class="min-h-screen bg-slate-50">
|
|
143
|
+
<view class="sticky top-0 z-10 bg-white shadow-sm">
|
|
144
|
+
<view class="flex items-center gap-3 px-4 py-3">
|
|
145
|
+
<view class="h-11 flex flex-1 items-center rounded-xl bg-slate-100 px-4">
|
|
146
|
+
<input
|
|
147
|
+
ref="inputRef"
|
|
148
|
+
v-model="searchQuery"
|
|
149
|
+
type="text"
|
|
150
|
+
placeholder="搜索菜单、功能..."
|
|
151
|
+
class="ml-2 flex-1 bg-transparent text-base text-slate-800 outline-none placeholder:text-slate-400"
|
|
152
|
+
/>
|
|
153
|
+
<view
|
|
154
|
+
v-if="searchQuery"
|
|
155
|
+
class="cursor-pointer rounded-full p-1 transition-colors hover:bg-slate-200"
|
|
156
|
+
@click="handleClear"
|
|
157
|
+
>
|
|
158
|
+
<wd-icon name="close-circle-filled" size="22px" color="#ccc" />
|
|
159
|
+
</view>
|
|
160
|
+
</view>
|
|
161
|
+
</view>
|
|
162
|
+
</view>
|
|
163
|
+
|
|
164
|
+
<view class="px-4 pb-4">
|
|
165
|
+
<SearchHistory v-model="searchHistory" @select="handleSelectHistory" />
|
|
166
|
+
|
|
167
|
+
<view v-if="filteredMenus.length === 0" class="mt-6">
|
|
168
|
+
<p class="text-center text-sm text-slate-400">未找到相关菜单</p>
|
|
169
|
+
</view>
|
|
170
|
+
|
|
171
|
+
<view v-else class="mt-4">
|
|
172
|
+
<p class="mb-3 text-xs text-slate-400">
|
|
173
|
+
{{ searchQuery.trim() ? `找到 ${filteredMenus.length} 个相关菜单` : '全部菜单' }}
|
|
174
|
+
</p>
|
|
175
|
+
<view class="space-y-2">
|
|
176
|
+
<view
|
|
177
|
+
v-for="menu in filteredMenus"
|
|
178
|
+
:key="menu.id"
|
|
179
|
+
class="flex cursor-pointer items-center gap-3 rounded-xl bg-white p-3 shadow-sm transition-all duration-200 active:scale-[0.98] hover:shadow-md"
|
|
180
|
+
@click="handleMenuClick(menu)"
|
|
181
|
+
>
|
|
182
|
+
<view
|
|
183
|
+
:style="{ background: menu.color }"
|
|
184
|
+
class="h-10 w-10 flex flex-shrink-0 items-center justify-center rounded-lg"
|
|
185
|
+
>
|
|
186
|
+
<!-- <component
|
|
187
|
+
v-if="iconMap[menu.icon]"
|
|
188
|
+
:is="iconMap[menu.icon]"
|
|
189
|
+
class="w-5 h-5 text-white"
|
|
190
|
+
/> -->
|
|
191
|
+
<wd-icon name="picture" size="22px" color="#FFF" />
|
|
192
|
+
</view>
|
|
193
|
+
<view class="flex-1 text-left">
|
|
194
|
+
<p class="text-base text-slate-800 font-medium" v-html="highlightedLabel(menu)" />
|
|
195
|
+
<p v-if="menu.tip" class="mt-0.5 text-xs text-slate-400">
|
|
196
|
+
{{ menu.tip }}
|
|
197
|
+
</p>
|
|
198
|
+
</view>
|
|
199
|
+
</view>
|
|
200
|
+
</view>
|
|
201
|
+
</view>
|
|
202
|
+
</view>
|
|
203
|
+
</view>
|
|
204
|
+
</template>
|