All checks were successful
Docker Build & Deploy / Build Docker Image (push) Successful in 18s
Docker Build & Deploy / Deploy to Production (push) Successful in 5s
Docker Build & Deploy / Cleanup Dangling Images (push) Successful in 1s
Docker Build & Deploy / WeChat Notification (push) Successful in 2s
- 将 BaseChart.vue、Icon.vue、IconSelector.vue 从 TypeScript 转换为 JavaScript - 移除 interface 声明,改用 defineProps 对象语法 - 移除类型注解,保持 JavaScript 兼容性 - 修复 ESLint 解析错误,现在所有 lint 检查通过
206 lines
4.4 KiB
Vue
206 lines
4.4 KiB
Vue
<template>
|
|
<PopupContainer
|
|
:show="show"
|
|
:title="title"
|
|
show-cancel-button
|
|
show-confirm-button
|
|
confirm-text="选择"
|
|
cancel-text="取消"
|
|
@update:show="emit('update:show', $event)"
|
|
@confirm="handleConfirm"
|
|
@cancel="handleCancel"
|
|
>
|
|
<div class="icon-selector">
|
|
<!-- 搜索框 -->
|
|
<van-search
|
|
v-model="searchKeyword"
|
|
placeholder="搜索图标"
|
|
:clearable="true"
|
|
@input="handleSearch"
|
|
/>
|
|
|
|
<!-- 图标列表 -->
|
|
<div
|
|
v-if="filteredIcons.length > 0"
|
|
class="icon-list"
|
|
>
|
|
<div
|
|
v-for="icon in paginatedIcons"
|
|
:key="icon.iconIdentifier"
|
|
class="icon-item"
|
|
:class="{ active: selectedIconIdentifier === icon.iconIdentifier }"
|
|
@click="handleSelectIcon(icon)"
|
|
>
|
|
<Icon
|
|
:icon-identifier="icon.iconIdentifier"
|
|
:size="32"
|
|
:color="selectedIconIdentifier === icon.iconIdentifier ? '#1989fa' : '#969799'"
|
|
/>
|
|
<span class="icon-label">{{ icon.iconName }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 无结果提示 -->
|
|
<van-empty
|
|
v-else
|
|
description="未找到匹配的图标"
|
|
/>
|
|
|
|
<!-- 分页 -->
|
|
<van-pagination
|
|
v-if="totalPages > 1"
|
|
v-model:current-page="currentPage"
|
|
:total-items="filteredIcons.length"
|
|
:items-per-page="pageSize"
|
|
class="pagination"
|
|
@change="handlePageChange"
|
|
/>
|
|
</div>
|
|
</PopupContainer>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { ref, computed, watch } from 'vue'
|
|
import { showToast } from 'vant'
|
|
import Icon from './Icon.vue'
|
|
import PopupContainer from './PopupContainer.vue'
|
|
|
|
const props = defineProps({
|
|
show: {
|
|
type: Boolean,
|
|
required: true
|
|
},
|
|
icons: {
|
|
type: Array,
|
|
required: true
|
|
},
|
|
title: {
|
|
type: String,
|
|
default: '选择图标'
|
|
},
|
|
defaultIconIdentifier: {
|
|
type: String,
|
|
default: ''
|
|
}
|
|
})
|
|
|
|
const emit = defineEmits(['update:show', 'confirm', 'cancel'])
|
|
|
|
const searchKeyword = ref('')
|
|
const currentPage = ref(1)
|
|
const pageSize = ref(20)
|
|
const selectedIconIdentifier = ref(props.defaultIconIdentifier)
|
|
|
|
// 搜索过滤
|
|
const filteredIcons = computed(() => {
|
|
if (!searchKeyword.value.trim()) {
|
|
return props.icons
|
|
}
|
|
|
|
const keyword = searchKeyword.value.toLowerCase().trim()
|
|
return props.icons.filter(icon =>
|
|
icon.iconName.toLowerCase().includes(keyword) ||
|
|
icon.collectionName.toLowerCase().includes(keyword) ||
|
|
icon.iconIdentifier.toLowerCase().includes(keyword)
|
|
)
|
|
})
|
|
|
|
// 分页
|
|
const totalPages = computed(() => Math.ceil(filteredIcons.value.length / pageSize.value))
|
|
|
|
const paginatedIcons = computed(() => {
|
|
const start = (currentPage.value - 1) * pageSize.value
|
|
const end = start + pageSize.value
|
|
return filteredIcons.value.slice(start, end)
|
|
})
|
|
|
|
const handleSearch = () => {
|
|
currentPage.value = 1
|
|
}
|
|
|
|
const handleSelectIcon = (icon) => {
|
|
selectedIconIdentifier.value = icon.iconIdentifier
|
|
}
|
|
|
|
const handlePageChange = (page) => {
|
|
currentPage.value = page
|
|
}
|
|
|
|
const handleConfirm = () => {
|
|
if (!selectedIconIdentifier.value) {
|
|
showToast('请选择一个图标')
|
|
return
|
|
}
|
|
emit('confirm', selectedIconIdentifier.value)
|
|
handleClose()
|
|
}
|
|
|
|
const handleCancel = () => {
|
|
emit('cancel')
|
|
handleClose()
|
|
}
|
|
|
|
const handleClose = () => {
|
|
searchKeyword.value = ''
|
|
currentPage.value = 1
|
|
selectedIconIdentifier.value = props.defaultIconIdentifier
|
|
}
|
|
|
|
// 监听默认图标变化
|
|
watch(() => props.defaultIconIdentifier, (newVal) => {
|
|
selectedIconIdentifier.value = newVal
|
|
})
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.icon-selector {
|
|
max-height: 70vh;
|
|
display: flex;
|
|
flex-direction: column;
|
|
|
|
.icon-list {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
max-height: 55vh;
|
|
padding: 16px;
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(80px, 1fr));
|
|
gap: 12px;
|
|
margin-bottom: 16px;
|
|
}
|
|
|
|
.icon-item {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 12px;
|
|
border: 2px solid #e5e7eb;
|
|
border-radius: 8px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
|
|
&:hover {
|
|
border-color: #1989fa;
|
|
background-color: #f5f5f5;
|
|
}
|
|
|
|
&.active {
|
|
border-color: #1989fa;
|
|
background-color: #e6f7ff;
|
|
}
|
|
}
|
|
|
|
.icon-label {
|
|
font-size: 12px;
|
|
color: #646464;
|
|
margin-top: 8px;
|
|
text-align: center;
|
|
}
|
|
|
|
.pagination {
|
|
padding: 16px;
|
|
border-top: 1px solid #e5e7eb;
|
|
}
|
|
}
|
|
</style>
|