feat: 页面优化
This commit is contained in:
parent
adedaecf29
commit
61821ccd55
File diff suppressed because it is too large
Load Diff
|
|
@ -8,17 +8,17 @@
|
|||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4",
|
||||
"pinia": "^2.1.6",
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"axios": "^1.5.0",
|
||||
"element-plus": "^2.3.14",
|
||||
"echarts": "^5.4.3",
|
||||
"@element-plus/icons-vue": "^2.1.0"
|
||||
"element-plus": "^2.3.14",
|
||||
"pinia": "^2.1.6",
|
||||
"vue": "^3.3.4",
|
||||
"vue-router": "^4.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.3.4",
|
||||
"vite": "^4.4.9",
|
||||
"sass": "^1.66.1"
|
||||
"sass": "^1.78.0",
|
||||
"vite": "^4.4.9"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
:title="isEdit ? '编辑字典' : '新增字典'"
|
||||
:visible="visible"
|
||||
width="600px"
|
||||
@close="handleClose"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="120px"
|
||||
>
|
||||
<el-form-item label="字典类型" prop="type">
|
||||
<el-input v-model="form.type" placeholder="请输入字典类型" :disabled="isEdit" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="字典名称" prop="name">
|
||||
<el-input v-model="form.name" placeholder="请输入字典名称" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="字典值" prop="value">
|
||||
<el-input v-model="form.value" placeholder="请输入字典值" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input-number v-model="form.sort" :min="0" :max="9999" controls-position="right" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="handleClose">取消</el-button>
|
||||
<el-button type="primary" @click="handleSubmit" :loading="submitting">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch } from 'vue'
|
||||
import { createDictionary, updateDictionary } from '@/api/dictionary'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dictionary: {
|
||||
type: Object,
|
||||
default: null
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['close', 'success'])
|
||||
|
||||
const formRef = ref(null)
|
||||
const submitting = ref(false)
|
||||
|
||||
const isEdit = computed(() => !!props.dictionary)
|
||||
|
||||
const form = reactive({
|
||||
type: '',
|
||||
name: '',
|
||||
value: '',
|
||||
sort: 0,
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
type: [
|
||||
{ required: true, message: '请输入字典类型', trigger: 'blur' }
|
||||
],
|
||||
name: [
|
||||
{ required: true, message: '请输入字典名称', trigger: 'blur' }
|
||||
],
|
||||
value: [
|
||||
{ required: true, message: '请输入字典值', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
|
||||
// 监听dictionary变化,填充表单
|
||||
watch(() => props.dictionary, (newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(form, {
|
||||
type: newVal.type || '',
|
||||
name: newVal.name || '',
|
||||
value: newVal.value || '',
|
||||
sort: newVal.sort || 0,
|
||||
remark: newVal.remark || ''
|
||||
})
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
Object.assign(form, {
|
||||
type: '',
|
||||
name: '',
|
||||
value: '',
|
||||
sort: 0,
|
||||
remark: ''
|
||||
})
|
||||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
// 关闭对话框
|
||||
const handleClose = () => {
|
||||
resetForm()
|
||||
emit('close')
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const handleSubmit = async () => {
|
||||
if (!formRef.value) return
|
||||
|
||||
await formRef.value.validate(async (valid) => {
|
||||
if (valid) {
|
||||
submitting.value = true
|
||||
try {
|
||||
const formData = { ...form }
|
||||
|
||||
if (isEdit.value) {
|
||||
await updateDictionary(props.dictionary.id, formData)
|
||||
ElMessage.success('更新成功')
|
||||
} else {
|
||||
await createDictionary(formData)
|
||||
ElMessage.success('创建成功')
|
||||
}
|
||||
|
||||
emit('success')
|
||||
} catch (error) {
|
||||
console.error('提交失败:', error)
|
||||
ElMessage.error('提交失败')
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
// 样式可以根据需要添加
|
||||
</style>
|
||||
|
|
@ -302,15 +302,6 @@ const rules = {
|
|||
]
|
||||
}
|
||||
|
||||
// 监听material变化,填充表单
|
||||
watch(() => props.material, (newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(form, newVal)
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
Object.assign(form, {
|
||||
|
|
@ -343,6 +334,15 @@ const resetForm = () => {
|
|||
formRef.value?.clearValidate()
|
||||
}
|
||||
|
||||
// 监听material变化,填充表单
|
||||
watch(() => props.material, (newVal) => {
|
||||
if (newVal) {
|
||||
Object.assign(form, newVal)
|
||||
} else {
|
||||
resetForm()
|
||||
}
|
||||
}, { immediate: true })
|
||||
|
||||
// 关闭对话框
|
||||
const handleClose = () => {
|
||||
resetForm()
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
<el-row :gutter="20">
|
||||
<!-- 统计卡片 -->
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<el-card class="stat-card stat-card-hover" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #409EFF;">
|
||||
<div class="stat-icon stat-icon-pulse" style="background-color: #409EFF;">
|
||||
<el-icon :size="30"><Box /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ overviewData.total_materials || 0 }}</div>
|
||||
<div class="stat-value stat-value-animate">{{ overviewData.total_materials || 0 }}</div>
|
||||
<div class="stat-label">材料总数</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -17,13 +17,13 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<el-card class="stat-card stat-card-hover" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #67C23A;">
|
||||
<div class="stat-icon stat-icon-pulse" style="background-color: #67C23A;">
|
||||
<el-icon :size="30"><Grid /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ overviewData.total_material_categories || 0 }}</div>
|
||||
<div class="stat-value stat-value-animate">{{ overviewData.total_material_categories || 0 }}</div>
|
||||
<div class="stat-label">材料种类</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -31,13 +31,13 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<el-card class="stat-card stat-card-hover" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #E6A23C;">
|
||||
<div class="stat-icon stat-icon-pulse" style="background-color: #E6A23C;">
|
||||
<el-icon :size="30"><OfficeBuilding /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ overviewData.total_brands || 0 }}</div>
|
||||
<div class="stat-value stat-value-animate">{{ overviewData.total_brands || 0 }}</div>
|
||||
<div class="stat-label">品牌数</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -45,13 +45,13 @@
|
|||
</el-col>
|
||||
|
||||
<el-col :span="6">
|
||||
<el-card class="stat-card">
|
||||
<el-card class="stat-card stat-card-hover" shadow="hover">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon" style="background-color: #F56C6C;">
|
||||
<div class="stat-icon stat-icon-pulse" style="background-color: #F56C6C;">
|
||||
<el-icon :size="30"><Document /></el-icon>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{ overviewData.cases_list?.length || 0 }}</div>
|
||||
<div class="stat-value stat-value-animate">{{ overviewData.cases_list?.length || 0 }}</div>
|
||||
<div class="stat-label">应用案例</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -114,16 +114,36 @@
|
|||
<el-row :gutter="20" class="charts-row">
|
||||
<!-- 应用案例列表 -->
|
||||
<el-col :span="24">
|
||||
<el-card>
|
||||
<el-card class="case-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>应用案例列表</span>
|
||||
<el-tag type="info" size="small">共 {{ overviewData.cases_list?.length || 0 }} 条</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="overviewData.cases_list || []" stripe>
|
||||
<el-table-column prop="name" label="材料名称" />
|
||||
<el-table-column prop="factory__factory_name" label="所属品牌" />
|
||||
<el-table-column prop="cases" label="案例说明" show-overflow-tooltip />
|
||||
<el-table
|
||||
:data="overviewData.cases_list || []"
|
||||
stripe
|
||||
class="case-table"
|
||||
:header-cell-style="{background:'#f5f7fa', color:'#606266'}"
|
||||
:row-style="{height: '60px'}"
|
||||
:cell-style="{padding: '12px 0'}"
|
||||
>
|
||||
<el-table-column prop="name" label="材料名称" width="200">
|
||||
<template #default="scope">
|
||||
<div class="material-name">{{ scope.row.name }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="factory__factory_name" label="所属品牌" width="200">
|
||||
<template #default="scope">
|
||||
<el-tag size="small" type="primary">{{ scope.row.factory__factory_name }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="cases" label="案例说明" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<div class="case-description">{{ scope.row.cases }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
|
@ -190,28 +210,55 @@ const updateCharts = (data) => {
|
|||
majorCategoryChartInstance.setOption({
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)'
|
||||
formatter: '{a} <br/>{b}: {c} ({d}%)',
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left'
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '专业类别',
|
||||
type: 'pie',
|
||||
radius: '50%',
|
||||
data: data.major_category_stats.map(item => ({
|
||||
value: item.count,
|
||||
name: item.major_category
|
||||
})),
|
||||
radius: ['40%', '70%'],
|
||||
avoidLabelOverlap: false,
|
||||
itemStyle: {
|
||||
borderRadius: 10,
|
||||
borderColor: '#fff',
|
||||
borderWidth: 2
|
||||
},
|
||||
label: {
|
||||
show: false,
|
||||
position: 'center'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
show: true,
|
||||
fontSize: 16,
|
||||
fontWeight: 'bold'
|
||||
},
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)'
|
||||
}
|
||||
}
|
||||
},
|
||||
labelLine: {
|
||||
show: false
|
||||
},
|
||||
data: data.major_category_stats.map(item => ({
|
||||
value: item.count,
|
||||
name: item.major_category
|
||||
}))
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
@ -222,6 +269,12 @@ const updateCharts = (data) => {
|
|||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
|
|
@ -231,17 +284,53 @@ const updateCharts = (data) => {
|
|||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
data: data.material_subcategory_stats.map(item => item.material_subcategory)
|
||||
data: data.material_subcategory_stats.map(item => item.material_subcategory),
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#666'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '材料数量',
|
||||
type: 'bar',
|
||||
data: data.material_subcategory_stats.map(item => item.count)
|
||||
data: data.material_subcategory_stats.map(item => item.count),
|
||||
itemStyle: {
|
||||
borderRadius: [0, 4, 4, 0],
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#83bff6' },
|
||||
{ offset: 0.5, color: '#188df0' },
|
||||
{ offset: 1, color: '#188df0' }
|
||||
])
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 1, 0, [
|
||||
{ offset: 0, color: '#2378f7' },
|
||||
{ offset: 0.7, color: '#2378f7' },
|
||||
{ offset: 1, color: '#83bff6' }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
@ -252,6 +341,12 @@ const updateCharts = (data) => {
|
|||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
|
|
@ -265,17 +360,49 @@ const updateCharts = (data) => {
|
|||
data: data.brand_stats.map(item => item.factory__factory_name),
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 30
|
||||
rotate: 30,
|
||||
color: '#666'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '材料数量',
|
||||
type: 'bar',
|
||||
data: data.brand_stats.map(item => item.count)
|
||||
data: data.brand_stats.map(item => item.count),
|
||||
itemStyle: {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#67C23A' },
|
||||
{ offset: 1, color: '#85CE61' }
|
||||
])
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#529B2E' },
|
||||
{ offset: 1, color: '#67C23A' }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
@ -286,6 +413,12 @@ const updateCharts = (data) => {
|
|||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||
borderColor: '#eee',
|
||||
borderWidth: 1,
|
||||
textStyle: {
|
||||
color: '#333'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
|
|
@ -299,17 +432,49 @@ const updateCharts = (data) => {
|
|||
data: data.region_stats.map(item => `${item.province} ${item.city}`),
|
||||
axisLabel: {
|
||||
interval: 0,
|
||||
rotate: 30
|
||||
rotate: 30,
|
||||
color: '#666'
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#ddd'
|
||||
}
|
||||
},
|
||||
axisTick: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value'
|
||||
type: 'value',
|
||||
axisLine: {
|
||||
show: false
|
||||
},
|
||||
splitLine: {
|
||||
lineStyle: {
|
||||
color: '#f0f0f0'
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '工厂数量',
|
||||
type: 'bar',
|
||||
data: data.region_stats.map(item => item.count)
|
||||
data: data.region_stats.map(item => item.count),
|
||||
itemStyle: {
|
||||
borderRadius: [4, 4, 0, 0],
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#E6A23C' },
|
||||
{ offset: 1, color: '#F3D19E' }
|
||||
])
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#CF8E2F' },
|
||||
{ offset: 1, color: '#E6A23C' }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
|
@ -356,8 +521,24 @@ onBeforeUnmount(() => {
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.dashboard {
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
margin-bottom: 20px;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #EBEEF5;
|
||||
|
||||
&.stat-card-hover:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
|
|
@ -372,6 +553,11 @@ onBeforeUnmount(() => {
|
|||
align-items: center;
|
||||
color: #fff;
|
||||
margin-right: 20px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.stat-icon-pulse:hover {
|
||||
animation: pulse 1.5s infinite;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
|
|
@ -382,6 +568,11 @@ onBeforeUnmount(() => {
|
|||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin-bottom: 5px;
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&.stat-value-animate {
|
||||
animation: fadeInUp 0.5s ease;
|
||||
}
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
|
|
@ -399,6 +590,65 @@ onBeforeUnmount(() => {
|
|||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.case-card {
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.case-table {
|
||||
.material-name {
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.case-description {
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
:deep(.el-table__row) {
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
background-color: #f5f7fa;
|
||||
transform: translateX(5px);
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-table__body tr:hover > td) {
|
||||
background-color: #f5f7fa !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.05);
|
||||
box-shadow: 0 0 0 10px rgba(255, 255, 255, 0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -10,6 +10,13 @@ export default defineConfig({
|
|||
'@': resolve(__dirname, 'src')
|
||||
}
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
api: 'modern-compiler' // 使用现代 Sass API 以消除弃用警告
|
||||
}
|
||||
}
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
proxy: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue