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
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:
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user