fix: 修复 TypeScript interface 语法错误
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 检查通过
This commit is contained in:
SunCheng
2026-02-18 22:09:19 +08:00
parent c49f66757e
commit 5e38a52e5b
3 changed files with 112 additions and 81 deletions

View File

@@ -1,10 +1,22 @@
<template> <template>
<div class="base-chart" ref="chartContainer"> <div
<van-loading v-if="loading" size="24px" vertical>加载中...</van-loading> ref="chartContainer"
<van-empty v-else-if="isEmpty" description="暂无数据" /> class="base-chart"
>
<van-loading
v-if="loading"
size="24px"
vertical
>
加载中...
</van-loading>
<van-empty
v-else-if="isEmpty"
description="暂无数据"
/>
<component <component
v-else
:is="chartComponent" :is="chartComponent"
v-else
:data="data" :data="data"
:options="mergedOptions" :options="mergedOptions"
:plugins="chartPlugins" :plugins="chartPlugins"
@@ -13,8 +25,8 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup>
import { ref, computed, watch, onMounted, onUnmounted } from 'vue' import { ref, computed, onMounted, onUnmounted } from 'vue'
import { Line, Bar, Pie, Doughnut } from 'vue-chartjs' import { Line, Bar, Pie, Doughnut } from 'vue-chartjs'
import { import {
Chart as ChartJS, Chart as ChartJS,
@@ -45,25 +57,33 @@ ChartJS.register(
Filler Filler
) )
interface Props { const props = defineProps({
type: 'line' | 'bar' | 'pie' | 'doughnut' type: {
data: any type: String,
options?: any required: true,
plugins?: any[] validator: (value) => ['line', 'bar', 'pie', 'doughnut'].includes(value)
loading?: boolean },
} data: {
type: Object,
const props = withDefaults(defineProps<Props>(), { required: true
options: () => ({}), },
plugins: () => [], options: {
loading: false type: Object,
default: () => ({})
},
plugins: {
type: Array,
default: () => []
},
loading: {
type: Boolean,
default: false
}
}) })
const emit = defineEmits<{ const emit = defineEmits(['chart:render'])
(e: 'chart:render', chart: any): void
}>()
const chartContainer = ref<HTMLDivElement>() const chartContainer = ref()
const { getChartOptions } = useChartTheme() const { getChartOptions } = useChartTheme()
// 图表组件映射 // 图表组件映射
@@ -79,8 +99,8 @@ const chartComponent = computed(() => {
// 检查是否为空数据 // 检查是否为空数据
const isEmpty = computed(() => { const isEmpty = computed(() => {
if (!props.data || !props.data.datasets) return true if (!props.data || !props.data.datasets) {return true}
return props.data.datasets.length === 0 || props.data.datasets.every((ds: any) => !ds.data || ds.data.length === 0) return props.data.datasets.length === 0 || props.data.datasets.every((ds) => !ds.data || ds.data.length === 0)
}) })
// 合并配置项 // 合并配置项
@@ -94,10 +114,10 @@ const chartPlugins = computed(() => {
}) })
// 响应式处理:监听容器大小变化 // 响应式处理:监听容器大小变化
let resizeObserver: ResizeObserver | null = null let resizeObserver = null
onMounted(() => { onMounted(() => {
if (!chartContainer.value) return if (!chartContainer.value) {return}
resizeObserver = new ResizeObserver(() => { resizeObserver = new ResizeObserver(() => {
// Chart.js 会自动处理 resize这里只是确保容器正确 // Chart.js 会自动处理 resize这里只是确保容器正确
@@ -114,7 +134,7 @@ onUnmounted(() => {
}) })
// 图表渲染完成回调 // 图表渲染完成回调
const onChartRender = (chart: any) => { const onChartRender = (chart) => {
emit('chart:render', chart) emit('chart:render', chart)
} }
</script> </script>

View File

@@ -3,30 +3,38 @@
class="iconify" class="iconify"
:data-icon="iconIdentifier" :data-icon="iconIdentifier"
:style="iconStyle" :style="iconStyle"
></span> />
</template> </template>
<script setup lang="ts"> <script setup>
import { computed } from 'vue' import { computed } from 'vue'
interface Props { const props = defineProps({
iconIdentifier: string iconIdentifier: {
width?: string | number type: String,
height?: string | number required: true
color?: string },
size?: string | number width: {
} type: [String, Number],
default: '1em'
const props = withDefaults(defineProps<Props>(), { },
width: '1em', height: {
height: '1em', type: [String, Number],
color: undefined, default: '1em'
size: undefined },
color: {
type: String,
default: undefined
},
size: {
type: [String, Number],
default: undefined
}
}) })
const iconStyle = computed(() => { const iconStyle = computed(() => {
const style: Record<string, string> = {} const style = {}
if (props.width) { if (props.width) {
style.width = typeof props.width === 'number' ? `${props.width}px` : props.width style.width = typeof props.width === 'number' ? `${props.width}px` : props.width
} }
@@ -40,7 +48,7 @@ const iconStyle = computed(() => {
const size = typeof props.size === 'number' ? `${props.size}px` : props.size const size = typeof props.size === 'number' ? `${props.size}px` : props.size
style.fontSize = size style.fontSize = size
} }
return style return style
}) })
</script> </script>

View File

@@ -20,7 +20,10 @@
/> />
<!-- 图标列表 --> <!-- 图标列表 -->
<div class="icon-list" v-if="filteredIcons.length > 0"> <div
v-if="filteredIcons.length > 0"
class="icon-list"
>
<div <div
v-for="icon in paginatedIcons" v-for="icon in paginatedIcons"
:key="icon.iconIdentifier" :key="icon.iconIdentifier"
@@ -38,50 +41,50 @@
</div> </div>
<!-- 无结果提示 --> <!-- 无结果提示 -->
<van-empty v-else description="未找到匹配的图标" /> <van-empty
v-else
description="未找到匹配的图标"
/>
<!-- 分页 --> <!-- 分页 -->
<van-pagination <van-pagination
v-if="totalPages > 1" v-if="totalPages > 1"
v-model:currentPage="currentPage" v-model:current-page="currentPage"
:total-items="filteredIcons.length" :total-items="filteredIcons.length"
:items-per-page="pageSize" :items-per-page="pageSize"
@change="handlePageChange"
class="pagination" class="pagination"
@change="handlePageChange"
/> />
</div> </div>
</PopupContainer> </PopupContainer>
</template> </template>
<script setup lang="ts"> <script setup>
import { ref, computed, watch } from 'vue' import { ref, computed, watch } from 'vue'
import { showToast } from 'vant' import { showToast } from 'vant'
import Icon from './Icon.vue' import Icon from './Icon.vue'
import PopupContainer from './PopupContainer.vue' import PopupContainer from './PopupContainer.vue'
interface Icon { const props = defineProps({
iconIdentifier: string show: {
iconName: string type: Boolean,
collectionName: string required: true
} },
icons: {
interface Props { type: Array,
show: boolean required: true
icons: Icon[] },
title?: string title: {
defaultIconIdentifier?: string type: String,
} default: '选择图标'
},
const props = withDefaults(defineProps<Props>(), { defaultIconIdentifier: {
title: '选择图标', type: String,
defaultIconIdentifier: '' default: ''
}
}) })
const emit = defineEmits<{ const emit = defineEmits(['update:show', 'confirm', 'cancel'])
'update:show': [value: boolean]
confirm: [iconIdentifier: string]
cancel: []
}>()
const searchKeyword = ref('') const searchKeyword = ref('')
const currentPage = ref(1) const currentPage = ref(1)
@@ -93,9 +96,9 @@ const filteredIcons = computed(() => {
if (!searchKeyword.value.trim()) { if (!searchKeyword.value.trim()) {
return props.icons return props.icons
} }
const keyword = searchKeyword.value.toLowerCase().trim() const keyword = searchKeyword.value.toLowerCase().trim()
return props.icons.filter(icon => return props.icons.filter(icon =>
icon.iconName.toLowerCase().includes(keyword) || icon.iconName.toLowerCase().includes(keyword) ||
icon.collectionName.toLowerCase().includes(keyword) || icon.collectionName.toLowerCase().includes(keyword) ||
icon.iconIdentifier.toLowerCase().includes(keyword) icon.iconIdentifier.toLowerCase().includes(keyword)
@@ -115,11 +118,11 @@ const handleSearch = () => {
currentPage.value = 1 currentPage.value = 1
} }
const handleSelectIcon = (icon: Icon) => { const handleSelectIcon = (icon) => {
selectedIconIdentifier.value = icon.iconIdentifier selectedIconIdentifier.value = icon.iconIdentifier
} }
const handlePageChange = (page: number) => { const handlePageChange = (page) => {
currentPage.value = page currentPage.value = page
} }
@@ -154,7 +157,7 @@ watch(() => props.defaultIconIdentifier, (newVal) => {
max-height: 70vh; max-height: 70vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
.icon-list { .icon-list {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;
@@ -165,7 +168,7 @@ watch(() => props.defaultIconIdentifier, (newVal) => {
gap: 12px; gap: 12px;
margin-bottom: 16px; margin-bottom: 16px;
} }
.icon-item { .icon-item {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@@ -175,25 +178,25 @@ watch(() => props.defaultIconIdentifier, (newVal) => {
border-radius: 8px; border-radius: 8px;
cursor: pointer; cursor: pointer;
transition: all 0.2s; transition: all 0.2s;
&:hover { &:hover {
border-color: #1989fa; border-color: #1989fa;
background-color: #f5f5f5; background-color: #f5f5f5;
} }
&.active { &.active {
border-color: #1989fa; border-color: #1989fa;
background-color: #e6f7ff; background-color: #e6f7ff;
} }
} }
.icon-label { .icon-label {
font-size: 12px; font-size: 12px;
color: #646464; color: #646464;
margin-top: 8px; margin-top: 8px;
text-align: center; text-align: center;
} }
.pagination { .pagination {
padding: 16px; padding: 16px;
border-top: 1px solid #e5e7eb; border-top: 1px solid #e5e7eb;