* width * explicitWidth * measuredWidth * percentWidth * scaleX

本文详细解释了Flash和Flex中各类对象的尺寸属性,包括width和height等,并阐述了这些属性在不同对象类型中的作用及相互之间的关系。

 

第一种情况是一般的flash.display.DisplayObject,包括flash.display.Shape、flash.display.MovieClip、flash.display.Sprite等,其width和height代表该对象的外接矩形的实际长宽像素数。等一下,这个定义还不严谨,严谨的说法应该是:如果这个DisplayObject的父容器、爷爷容器,一直到祖宗容器也就是到Stage,全部都没有缩放,那么这个width和height就能代表该对象的外接矩形的实际长宽像素数。之所以要加上这个前提,是因为《Flex 2、Flex 3和Flash 9中的缩放》提到过,父容器的缩放并不会改变子对象的属性,而只会改变子对象在屏幕上的实际渲染尺寸。

虽然父容器的缩放并不会改变子对象的属性,但是DisplayObject对象自己的缩放属性(scaleX和scaleY)却会影响自己的width和height. 设置DisplayObject的scaleX和scaleY会改变其width和height,而且,设置width和height也会改变scaleX和scaleY. 这就意味着,设置一般的DisplayObject的width和height就会导致该对象缩放。不过,如果这个DisplayObject原本的width和height为0,则无法改变它的width和height,比如一个空无一物的Sprite,不论如何设置,width和height始终是0.

第二种情况是flash.display.TextField,它重写了基类的set width和set height函数,所以改变TextField的width和height并不会令TextField缩放,而只是改变文本区域的大小。改变width和height时,单个文字不会变大或变小,而可显示的文字数量会变多或变少。

改变TextField的width和height不会改变TextField的scaleX和scaleY,但是改变TextField的scaleX和scaleY会改变TextField的width和height.

第三种情况是Flex的mx.core.UIComponent,它的width和height不代表实际尺寸,而代表“占据”的尺寸,即使是一个空无一物的VBox,也可以设置其width和height(这一点与Sprite不同).

不过UIComponent有一点和TextField类似,改变UIComponent的width和height不会改变UIComponent的scaleX和scaleY,但是改变UIComponent的scaleX和scaleY会改变width和height. UIComponent中有unscaledWidth和unscaledHeight两个属性。unscaledWidth等于width/scaleX, unscaledHeight等于height/scaleY.

因为UICompoent涉及Flex的布局,所以其中还有许多尺寸相关的属性和函数,可以用来自动计算布局。这里就不一一介绍了。

最后说一下MXML中的width属性(attribute),它其实对应了该组件在ActionScript中的percentWidth和width两个属性(property);而MXML中的height属性(attribute)也对应了ActionScript中的percentHeight和height两个属性(property)。


明白这些属性之后,自然容易理解剩余的属性。 

  * width,组件的当前实际宽度,以像素为单位。为什么会有当前实际宽度的这样的说法呢?因为在Flex中,能够为组件设置相对宽度,即百分比的表示宽度,这时组件当前的实际宽度是根据父容器的宽度动态决定的,开发者无法知道实际宽度的,因此通过读取width属性可以知道其当前实际宽度。 

  * measuredWidth是组件的缺省宽度,以像素为单位。可以通过重载组件的measure方法来改变它的值,一般情况下,组件的measuredWidth都是0。 

  * explicitWidth 是显式指定的宽度,以像素为单位。所谓显式指定宽度区别于动态的相对宽度,即区别于百分比宽度percentWidth。开发者设置该属性值时就已经知道到组件在运行时的宽度,故称为显式指定的宽度。该属性同百分比宽度互斥,一旦设置了explicitWidth,则percentWidth的值就会变为 NaN,同理,一旦设置了percentWidth,explicitWidth值就会变为NaN。当用ActionsSript代码设置了组件的 width属性值时,explicitWidth属性会立即被赋予同样的值,同时percentWidth会变为NaN。设置explicitWidth 属性时,percentWidth会立即被设置为NaN,但是width属性则不会立即改变,要延迟组件布局的commit阶段才会变为与 explicitWidth相同的值。 

  * percentWidth是指组件的百分比宽度,比如设置组件占用父容器的50%的宽度,那么就设置该属性值为50。该属性同显式指定的宽度(explicitWidth)互斥,一旦设置该属性值,则explicitWidth属性立即变为NaN。 

  * scaleX是当前组件宽度的缩放比例。一旦设置属性,考虑到性能,不会立即更新组件的width和explicitWidth属性,延迟到explicitWidth时会将width和explicitWidth属性值改变。 


  除了以上基本属性之外,值得注意的就是组件MXML标记中的width属性。这个属性在编译组件MXML标记时,根据标记的该属性值的格式编译成设置 percentWidth或width属性的AS代码。如果组件MXML标记的width属性后面有百分号(%),则其被编译为设置 percentWidth属性的AS代码,如果组件MXML标记的width后面不带百分号(%),则其被编译为设置width属性的AS代码。

 

<template> <Layout> <div class="score-container"> <div class="score-header"> <h2>成绩查询</h2> <p>查看你的考试成绩和学习进度</p> </div> <!-- 增强的筛选区域 --> <div class="score-filters"> <el-select v-model="selectedSubject" placeholder="选择科目" class="subject-filter" @change="handleFilterChange" > <el-option label="全部科目" value="" /> <el-option label="前端开发" value="frontend" /> <el-option label="后端开发" value="backend" /> <el-option label="数据库" value="database" /> </el-select> <el-select v-model="scoreRange" placeholder="成绩范围" class="range-filter" @change="handleFilterChange" > <el-option label="全部成绩" value="" /> <el-option label="未通过(0-59)" value="fail" /> <el-option label="及格(60-79)" value="pass" /> <el-option label="优秀(80-100)" value="excellent" /> </el-select> <el-date-picker v-model="dateRange" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" class="date-filter" @change="handleFilterChange" /> </div> <!-- 加载状态 --> <el-skeleton v-if="loading" class="score-skeleton" :rows="8" /> <template v-else> <el-card class="score-overview"> <div slot="header"> <h3>成绩概览</h3> </div> <div class="overview-stats"> <div class="stat-item"> <div class="stat-value">{{ totalExams }}</div> <div class="stat-label">总考试次数</div> </div> <div class="stat-item"> <div class="stat-value">{{ passRate }}%</div> <div class="stat-label">通过率</div> </div> <div class="stat-item"> <div class="stat-value">{{ avgScore }}</div> <div class="stat-label">平均得分</div> </div> <div class="stat-item"> <div class="stat-value">{{ highestScore }}</div> <div class="stat-label">最高得分</div> </div> </div> <!-- 优化的成绩趋势图 --> <div class="score-chart"> <h4>成绩趋势</h4> <el-card> <template #header> <div class="chart-header"> <span>成绩趋势图</span> <el-select v-model="chartType" size="small" @change="updateChart" > <el-option label="折线图" value="line" /> <el-option label="柱状图" value="bar" /> </el-select> </div> </template> <v-chart :option="chartOption" height="400px" /> </el-card> </div> </el-card> <!-- 成绩明细表格(带分页) --> <el-card class="score-list" style="margin-top: 20px;"> <div slot="header"> <h3>考试成绩明细</h3> </div> <!-- 空状态提示 --> <el-empty v-if="filteredScores.length === 0" description="没有找到匹配的成绩记录" class="empty-state" /> <el-table v-else :data="pagedScores" border style="width: 100%" :default-sort="{prop: 'examDate', order: 'descending'}" @sort-change="handleSortChange" row-class-name="table-row-class" > <el-table-column prop="examId" label="考试ID" width="100" /> <el-table-column prop="title" label="考试名称" sortable /> <el-table-column prop="subjectName" label="科目" width="120" /> <el-table-column prop="score" label="得分" width="100" sortable > <template #default="scope"> <span :class="scope.row.score >= 60 ? 'pass-score' : 'fail-score'"> {{ scope.row.score }} </span> </template> </el-table-column> <el-table-column prop="totalScore" label="总分" width="80" /> <el-table-column prop="rank" label="排名" width="80" sortable > <template #default="scope"> <el-badge :value="scope.row.rank" type="primary" /> </template> </el-table-column> <el-table-column prop="examDate" label="考试日期" width="180" sortable /> <el-table-column prop="duration" label="用时(分钟)" width="120" /> <el-table-column label="操作" width="220"> <template #default="scope"> <el-button type="primary" size="small" @click="viewDetails(scope.row.examId)" style="margin-right: 5px;" > 详情 </el-button> <el-button type="success" size="small" @click="viewAnalysis(scope.row.examId)" style="margin-right: 5px;" > 分析 </el-button> <el-button type="warning" size="small" @click="retryExam(scope.row.examId)" :disabled="scope.row.score >= 60" > 重考 </el-button> </template> </el-table-column> </el-table> <!-- 分页控件 --> <div class="pagination-container" v-if="filteredScores.length > 0"> <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="currentPage" :page-sizes="[5, 10, 20]" :page-size="pageSize" :total="filteredScores.length" layout="total, sizes, prev, pager, next, jumper" /> </div> </el-card> </template> </div> </Layout> </template> <script setup lang="ts"> import { ref, computed, onMounted, watch } from 'vue' import { useRouter } from 'vue-router' import VChart from 'vue-echarts' import { use } from 'echarts/core' import { LineChart, BarChart } from 'echarts/charts' import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' import { CanvasRenderer } from 'echarts/renderers' import Layout from '@/components/layout/Layout.vue' import { ElMessage } from 'element-plus' // 注册所需的图表模块 use([LineChart, BarChart, GridComponent, TooltipComponent, LegendComponent, CanvasRenderer]) // 类型定义 interface ScoreItem { examId: string title: string subject: string subjectName: string score: number totalScore: number rank: number examDate: string duration: number allowRetry?: boolean // 是否允许重考 } // 图表配置 const chartOption = ref<any>({ tooltip: { trigger: 'axis' }, legend: { data: ['你的成绩', '平均分'] }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true }, xAxis: { type: 'category', data: [] }, yAxis: { type: 'value', min: 0, max: 100, axisLabel: { formatter: '{value} 分' } }, series: [ { name: '你的成绩', type: 'line', data: [], markPoint: { data: [ { type: 'max', name: '最高分' }, { type: 'min', name: '最低分' } ] }, markLine: { data: [ { type: 'average', name: '平均分' } ] } }, { name: '平均分', type: 'line', data: [], lineStyle: { type: 'dashed' } } ] }) const router = useRouter() // 筛选条件 const selectedSubject = ref('') const dateRange = ref<any>(null) const scoreRange = ref('') // 新增:成绩范围筛选 const currentPage = ref(1) const pageSize = ref(10) const sortProp = ref('examDate') const sortOrder = ref('descending') const chartType = ref('line') // 图表类型切换 const loading = ref(false) // 成绩数据 const scoreData = ref<ScoreItem[]>([ { examId: 'e001', title: 'JavaScript 基础测试', subject: 'frontend', subjectName: '前端开发', score: 85, totalScore: 100, rank: 3, examDate: '2023-09-15', duration: 45, allowRetry: true }, { examId: 'e002', title: 'HTML/CSS 技能考核', subject: 'frontend', subjectName: '前端开发', score: 78, totalScore: 100, rank: 7, examDate: '2023-09-20', duration: 55, allowRetry: true }, { examId: 'e003', title: 'Node.js 入门测试', subject: 'backend', subjectName: '后端开发', score: 58, totalScore: 100, rank: 15, examDate: '2023-09-25', duration: 60, allowRetry: true }, { examId: 'e004', title: 'MySQL 基础测试', subject: 'database', subjectName: '数据库', score: 92, totalScore: 100, rank: 1, examDate: '2023-10-05', duration: 40, allowRetry: false } ]) // 模拟平均分数据(实际项目可从接口获取) const avgScoreData = ref([72, 75, 60, 88]) // 处理筛选变化 const handleFilterChange = () => { currentPage.value = 1 // 筛选变化时重置到第一页 loading.value = true // 模拟加载延迟 setTimeout(() => { loading.value = false updateChart() // 筛选后更新图表 }, 500) } // 筛选后的成绩 const filteredScores = computed<ScoreItem[]>(() => { return scoreData.value.filter(item => { // 科目筛选 const subjectMatch = !selectedSubject.value || item.subject === selectedSubject.value // 日期筛选 let dateMatch = true if (dateRange.value && dateRange.value.length === 2) { const examDate = new Date(item.examDate) dateMatch = examDate >= dateRange.value[0] && examDate <= dateRange.value[1] } // 成绩范围筛选 let scoreMatch = true if (scoreRange.value) { switch(scoreRange.value) { case 'fail': scoreMatch = item.score < 60 break case 'pass': scoreMatch = item.score >= 60 && item.score < 80 break case 'excellent': scoreMatch = item.score >= 80 break } } return subjectMatch && dateMatch && scoreMatch }).sort((a, b) => { // 排序处理 if (sortProp.value) { const prop = sortProp.value as keyof ScoreItem if (a[prop] < b[prop]) return sortOrder.value === 'ascending' ? -1 : 1 if (a[prop] > b[prop]) return sortOrder.value === 'ascending' ? 1 : -1 } return 0 }) }) // 分页处理 const pagedScores = computed(() => { const startIndex = (currentPage.value - 1) * pageSize.value return filteredScores.value.slice(startIndex, startIndex + pageSize.value) }) // 统计数据 const totalExams = computed(() => filteredScores.value.length) const passRate = computed(() => { if (totalExams.value === 0) return 0 const passCount = filteredScores.value.filter(item => item.score >= 60).length return Math.round((passCount / totalExams.value) * 100) }) const avgScore = computed(() => { if (totalExams.value === 0) return 0 const sum = filteredScores.value.reduce((acc, item) => acc + item.score, 0) return Math.round(sum / totalExams.value) }) const highestScore = computed(() => { if (totalExams.value === 0) return 0 return Math.max(...filteredScores.value.map(item => item.score)) }) // 图表数据更新 const updateChart = () => { const sortedData = [...filteredScores.value].sort( (a, b) => new Date(a.examDate).getTime() - new Date(b.examDate).getTime() ) chartOption.value.xAxis.data = sortedData.map(item => item.title) chartOption.value.series[0].data = sortedData.map(item => item.score) chartOption.value.series[0].type = chartType.value // 截取对应长度的平均分数据(实际项目应从接口获取对应考试的平均分) chartOption.value.series[1].data = avgScoreData.value.slice(0, sortedData.length) chartOption.value.series[1].type = chartType.value } // 表格排序 const handleSortChange = (sort: { prop: string, order: string }) => { sortProp.value = sort.prop sortOrder.value = sort.order === 'ascending' ? 'ascending' : 'descending' } // 分页事件 const handleSizeChange = (val: number) => { pageSize.value = val currentPage.value = 1 } const handleCurrentChange = (val: number) => { currentPage.value = val } // 查看详情 const viewDetails = (examId: string) => { router.push(`/score/details/${examId}`) } // 查看分析 const viewAnalysis = (examId: string) => { router.push(`/score/analysis/${examId}`) } // 重考功能 const retryExam = (examId: string) => { const exam = scoreData.value.find(item => item.examId === examId) if (exam && exam.allowRetry) { router.push(`/examination/${examId}?retry=true`) } else { ElMessage.warning('该考试不允许重考') } } // 初始加载 onMounted(() => { loading.value = true // 模拟数据加载 setTimeout(() => { loading.value = false updateChart() }, 800) }) // 监听筛选条件变化更新图表 watch([selectedSubject, dateRange, scoreRange], updateChart) </script> <style scoped> .score-container { padding: 20px; } .score-header { margin-bottom: 20px; } .score-header h2 { margin: 0 0 10px 0; color: #303133; } .score-header p { margin: 0; color: #606266; } .score-filters { display: flex; gap: 15px; margin-bottom: 20px; flex-wrap: wrap; } .subject-filter, .range-filter { width: 200px; } .date-filter { width: 350px; } .overview-stats { display: flex; justify-content: space-around; margin-bottom: 30px; flex-wrap: wrap; gap: 15px; } .stat-item { text-align: center; padding: 15px; flex: 1; min-width: 120px; background-color: #f5f7fa; border-radius: 6px; } .stat-value { font-size: 28px; font-weight: 700; color: #4b0082; margin-bottom: 5px; } .stat-label { color: #606266; font-size: 14px; } .score-chart { padding: 10px 0; } .score-chart h4 { margin: 0 0 15px 0; } .chart-header { display: flex; justify-content: space-between; align-items: center; } .pass-score { color: #67c23a; font-weight: 500; } .fail-score { color: #f56c6c; font-weight: 500; } .pagination-container { margin-top: 15px; text-align: right; } .empty-state { margin: 40px 0; } .score-skeleton { margin-bottom: 20px; } /* 表格行样式 */ :deep(.table-row-class) { &:nth-child(even) { background-color: #fafafa; } &:hover { background-color: #f5f7fa; } } /* 未通过行高亮 */ :deep(.el-table__row) { &:has(.fail-score) { background-color: #fff5f5 !important; } } </style>根据代码修复控制台告警[ECharts] Can't get DOM width or height. Please check dom.clientWidth and dom.clientHeight. They should not be 0.For example, you may need to call this in the callback of window.onload. outputLog @ chunk-F3YHV5CU.js?v=3fbc4ae4:604 warn @ chunk-F3YHV5CU.js?v=3fbc4ae4:611 init2 @ chunk-F3YHV5CU.js?v=3fbc4ae4:14540 init$1 @ vue-echarts.js?v=3fbc4ae4:243 (anonymous) @ vue-echarts.js?v=3fbc4ae4:365 (anonymous) @ chunk-JDSL2H4L.js?v=3fbc4ae4:4998 callWithErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2296 callWithAsyncErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2303 hook.__weh.hook.__weh @ chunk-JDSL2H4L.js?v=3fbc4ae4:4978 flushPostFlushCbs @ chunk-JDSL2H4L.js?v=3fbc4ae4:2481 flushJobs @ chunk-JDSL2H4L.js?v=3fbc4ae4:2523 Promise.then queueFlush @ chunk-JDSL2H4L.js?v=3fbc4ae4:2418 queueJob @ chunk-JDSL2H4L.js?v=3fbc4ae4:2413 effect2.scheduler @ chunk-JDSL2H4L.js?v=3fbc4ae4:7639 trigger @ chunk-JDSL2H4L.js?v=3fbc4ae4:533 endBatch @ chunk-JDSL2H4L.js?v=3fbc4ae4:591 notify @ chunk-JDSL2H4L.js?v=3fbc4ae4:853 trigger @ chunk-JDSL2H4L.js?v=3fbc4ae4:827 set value @ chunk-JDSL2H4L.js?v=3fbc4ae4:1706 (anonymous) @ ScoreInquiry.vue:479 setTimeout (anonymous) @ ScoreInquiry.vue:478 (anonymous) @ chunk-JDSL2H4L.js?v=3fbc4ae4:4998 callWithErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2296 callWithAsyncErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2303 hook.__weh.hook.__weh @ chunk-JDSL2H4L.js?v=3fbc4ae4:4978 flushPostFlushCbs @ chunk-JDSL2H4L.js?v=3fbc4ae4:2481 flushJobs @ chunk-JDSL2H4L.js?v=3fbc4ae4:2523 Promise.then queueFlush @ chunk-JDSL2H4L.js?v=3fbc4ae4:2418 queuePostFlushCb @ chunk-JDSL2H4L.js?v=3fbc4ae4:2432 queueEffectWithSuspense @ chunk-JDSL2H4L.js?v=3fbc4ae4:9523 baseWatchOptions.scheduler @ chunk-JDSL2H4L.js?v=3fbc4ae4:8402 effect2.scheduler @ chunk-JDSL2H4L.js?v=3fbc4ae4:2042 trigger @ chunk-JDSL2H4L.js?v=3fbc4ae4:533 endBatch @ chunk-JDSL2H4L.js?v=3fbc4ae4:591 notify @ chunk-JDSL2H4L.js?v=3fbc4ae4:853 trigger @ chunk-JDSL2H4L.js?v=3fbc4ae4:827 set value @ chunk-JDSL2H4L.js?v=3fbc4ae4:1706 finalizeNavigation @ vue-router.js?v=3fbc4ae4:2213 (anonymous) @ vue-router.js?v=3fbc4ae4:2151 Promise.then pushWithRedirect @ vue-router.js?v=3fbc4ae4:2138 push @ vue-router.js?v=3fbc4ae4:2089 handleMenuSelect @ Header.vue:176 callWithErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2296 callWithAsyncErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2303 emit @ chunk-JDSL2H4L.js?v=3fbc4ae4:8589 (anonymous) @ chunk-JDSL2H4L.js?v=3fbc4ae4:10300 handleMenuItemClick @ chunk-IO2JD3WY.js?v=3fbc4ae4:36331 handleClick @ chunk-IO2JD3WY.js?v=3fbc4ae4:36568 callWithErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2296 callWithAsyncErrorHandling @ chunk-JDSL2H4L.js?v=3fbc4ae4:2303 invoker @ chunk-JDSL2H4L.js?v=3fbc4ae4:11335
最新发布
10-27
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值