npmapps 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/app/Wscats.vue-1.0.26.vsix +0 -0
  2. package/app/febean.vue-format-0.1.8.vsix +0 -0
  3. package/app/wujie-vue3-child/.claude/settings.local.json +8 -0
  4. package/app/wujie-vue3-child/.vscode/extensions.json +3 -0
  5. package/app/wujie-vue3-child/PROJECT_MEMORY.md +427 -0
  6. package/app/wujie-vue3-child/README.md +5 -0
  7. package/app/wujie-vue3-child/index.html +13 -0
  8. package/app/wujie-vue3-child/package-lock.json +5744 -0
  9. package/app/wujie-vue3-child/package.json +28 -0
  10. package/app/wujie-vue3-child/public/vite.svg +1 -0
  11. package/app/wujie-vue3-child/src/App.vue +130 -0
  12. package/app/wujie-vue3-child/src/assets/vue.svg +1 -0
  13. package/app/wujie-vue3-child/src/components/HelloWorld.vue +43 -0
  14. package/app/wujie-vue3-child/src/components/tags-view.vue +193 -0
  15. package/app/wujie-vue3-child/src/components/tags-view1.vue +131 -0
  16. package/app/wujie-vue3-child/src/hooks/useClickOutside.js +11 -0
  17. package/app/wujie-vue3-child/src/hooks/useTableDragSort.js +28 -0
  18. package/app/wujie-vue3-child/src/main.js +15 -0
  19. package/app/wujie-vue3-child/src/router/index.js +104 -0
  20. package/app/wujie-vue3-child/src/store/tagsViewStroe.js +34 -0
  21. package/app/wujie-vue3-child/src/style.css +4 -0
  22. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/README.md +836 -0
  23. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/REFLEX_EXAMPLES.md +728 -0
  24. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentPersonnelSelector.jsx +687 -0
  25. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentPersonnelSelector.module.scss +560 -0
  26. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelector.jsx +570 -0
  27. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelector.module.scss +330 -0
  28. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelectorV2.jsx +378 -0
  29. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/DepartmentSelectorV2.module.scss +228 -0
  30. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/OptionsSelector.jsx +399 -0
  31. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/OptionsSelector.module.scss +252 -0
  32. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PersonnelSelector.jsx +585 -0
  33. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PersonnelSelector.module.scss +331 -0
  34. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PopoverSelector.jsx +392 -0
  35. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/PopoverSelector.module.scss +39 -0
  36. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/README.md +248 -0
  37. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/components/SelectorTrigger.jsx +194 -0
  38. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/index.jsx +1459 -0
  39. package/app/wujie-vue3-child/src/views/aiCoach/departmentPersonnel/mockData.js +301 -0
  40. package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/dialogueSegment/index.jsx +28 -4
  41. package/app/wujie-vue3-child/src/views/aiCoach/index.jsx +32 -0
  42. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ChartsPanel/index.jsx +121 -0
  43. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ChartsPanel/index.module.scss +76 -0
  44. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/DonutChart/index.jsx +104 -0
  45. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/PracticeTable/index.jsx +75 -0
  46. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/PracticeTable/index.module.scss +12 -0
  47. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankBarChart/index.jsx +62 -0
  48. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankBarChart/index.module.scss +43 -0
  49. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingGroup/index.jsx +29 -0
  50. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingGroup/index.module.scss +5 -0
  51. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingList/index.jsx +58 -0
  52. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/RankingList/index.module.scss +85 -0
  53. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ScriptStatsPanel/index.jsx +92 -0
  54. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/ScriptStatsPanel/index.module.scss +56 -0
  55. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/StatCardsRow/index.jsx +40 -0
  56. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/StatCardsRow/index.module.scss +53 -0
  57. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/echarts/EchartsDonut.jsx +106 -0
  58. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/components/echarts/EchartsRankBar.jsx +132 -0
  59. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/index.jsx +176 -0
  60. package/app/wujie-vue3-child/src/views/aiCoach/practiceStatus/index.module.scss +96 -0
  61. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/CoachReport/index.jsx +162 -0
  62. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/CoachReport/index.module.scss +16 -0
  63. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ComprehensiveEvaluation/index.jsx +29 -0
  64. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ComprehensiveEvaluation/index.module.scss +25 -0
  65. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueBubble/index.jsx +106 -0
  66. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueBubble/index.module.scss +164 -0
  67. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueRecord/index.jsx +182 -0
  68. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DialogueRecord/index.module.scss +203 -0
  69. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionDetail/index.jsx +145 -0
  70. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionDetail/index.module.scss +126 -0
  71. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionScores/index.jsx +67 -0
  72. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/DimensionScores/index.module.scss +105 -0
  73. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ReportHeader/index.jsx +81 -0
  74. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ReportHeader/index.module.scss +47 -0
  75. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/RoleInfo/index.jsx +64 -0
  76. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/RoleInfo/index.module.scss +85 -0
  77. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ScoreBadge/index.jsx +39 -0
  78. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/ScoreBadge/index.module.scss +44 -0
  79. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/SubDimensionItem/index.jsx +83 -0
  80. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/components/SubDimensionItem/index.module.scss +101 -0
  81. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/index.jsx +50 -0
  82. package/app/wujie-vue3-child/src/views/aiCoach/reportDetail/index.module.scss +25 -0
  83. package/app/wujie-vue3-child/src/views/child-to-parent.vue +117 -0
  84. package/app/wujie-vue3-child/src/views/home.vue +53 -0
  85. package/app/wujie-vue3-child/src/views/jsx/btnSelect/btnSelect.vue +169 -0
  86. package/app/wujie-vue3-child/src/views/jsx/btnSelect/index.vue +69 -0
  87. package/app/wujie-vue3-child/src/views/jsx/com.vue +44 -0
  88. package/app/wujie-vue3-child/src/views/jsx/dialog.jsx +66 -0
  89. package/app/wujie-vue3-child/src/views/jsx/index.vue +72 -0
  90. package/app/wujie-vue3-child/src/views/jsx/props.vue +33 -0
  91. package/app/wujie-vue3-child/src/views/parent-to-child.vue +225 -0
  92. package/app/wujie-vue3-child/src/views/phone-code.vue +318 -0
  93. package/app/wujie-vue3-child/src/views/router-jump.vue +123 -0
  94. package/app/wujie-vue3-child/src/views/test.vue +192 -0
  95. package/app/wujie-vue3-child/vite.config.js +15 -0
  96. package/package.json +1 -1
  97. package/app/aiCoach/index.jsx +0 -20
  98. package/npmapps-1.0.20.tgz +0 -0
  99. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/collapseExpand/index.jsx +0 -0
  100. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/collapseExpand/index.module.scss +0 -0
  101. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/dialogueSegment/index.module.scss +0 -0
  102. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/index.jsx +0 -0
  103. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/index.module.scss +0 -0
  104. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/inputColumn/index.jsx +0 -0
  105. /package/app/{aiCoach → wujie-vue3-child/src/views/aiCoach}/scriptTable/inputColumn/index.module.scss +0 -0
@@ -0,0 +1,53 @@
1
+ .cardsRow {
2
+ display: grid;
3
+ grid-template-columns: repeat(6, minmax(0, 1fr));
4
+ gap: 10px;
5
+ }
6
+
7
+ .card {
8
+ border: 1px solid #ebeef5;
9
+ border-radius: 8px;
10
+ padding: 12px 14px;
11
+ background: #fff;
12
+ }
13
+
14
+ .cardTitle {
15
+ font-size: 12px;
16
+ color: #606266;
17
+ margin-bottom: 6px;
18
+ }
19
+
20
+ .cardValue {
21
+ font-size: 20px;
22
+ font-weight: 700;
23
+ color: #303133;
24
+ }
25
+
26
+ .statCard {
27
+ border-radius: 8px;
28
+ border: 1px solid #ebeef5;
29
+ padding: 12px 14px;
30
+ background: #fff;
31
+ display: flex;
32
+ flex-direction: column;
33
+ }
34
+
35
+ .statCardTitle {
36
+ font-size: 12px;
37
+ color: #606266;
38
+ margin-bottom: 6px;
39
+ }
40
+
41
+ .statCardValue {
42
+ font-size: 20px;
43
+ font-weight: 700;
44
+ color: #303133;
45
+ }
46
+
47
+ /* bgClassList placeholders if needed, though not found in original CSS */
48
+ .cardBg1 {}
49
+ .cardBg2 {}
50
+ .cardBg3 {}
51
+ .cardBg4 {}
52
+ .cardBg5 {}
53
+ .cardBg6 {}
@@ -0,0 +1,106 @@
1
+ import { defineComponent, onMounted, ref, watch, onUnmounted } from 'vue'
2
+ import * as echarts from 'echarts'
3
+
4
+ export default defineComponent({
5
+ name: 'EchartsDonut',
6
+ props: {
7
+ series: {
8
+ type: Array,
9
+ default: () => [],
10
+ },
11
+ hiddenLegend: {
12
+ type: Array,
13
+ default: () => [],
14
+ },
15
+ height: {
16
+ type: Number,
17
+ default: 200,
18
+ },
19
+ },
20
+ setup(props) {
21
+ const chartRef = ref(null)
22
+ let chartInstance = null
23
+
24
+ const initChart = () => {
25
+ if (!chartRef.value) return
26
+ chartInstance = echarts.init(chartRef.value)
27
+ updateOption()
28
+ }
29
+
30
+ const updateOption = () => {
31
+ if (!chartInstance) return
32
+ // 过滤掉被隐藏的项
33
+ const data = props.series
34
+ .filter((item) => !props.hiddenLegend.includes(item.name))
35
+ .map((item) => ({
36
+ name: item.name,
37
+ value: item.value,
38
+ itemStyle: { color: item.color },
39
+ }))
40
+
41
+ // 计算总数
42
+ const total = data.reduce((sum, item) => sum + (Number(item.value) || 0), 0)
43
+
44
+ const option = {
45
+ tooltip: {
46
+ trigger: 'item',
47
+ formatter: '{b}: {c} ({d}%)',
48
+ },
49
+ legend: {
50
+ show: false, // 外部已有 Legend
51
+ },
52
+ series: [
53
+ {
54
+ name: '进度统计',
55
+ type: 'pie',
56
+ radius: ['55%', '90%'],
57
+ avoidLabelOverlap: true,
58
+ label: {
59
+ show: true,
60
+ position: 'inside',
61
+ formatter: '{c}({d}%)',
62
+ color: '#000',
63
+ fontSize: 12,
64
+ },
65
+ labelLine: {
66
+ show: false,
67
+ },
68
+ data: data,
69
+ },
70
+ ],
71
+ }
72
+ chartInstance.setOption(option)
73
+ }
74
+
75
+ watch(
76
+ () => [props.series, props.hiddenLegend],
77
+ () => {
78
+ updateOption()
79
+ },
80
+ { deep: true }
81
+ )
82
+
83
+ onMounted(() => {
84
+ initChart()
85
+ window.addEventListener('resize', handleResize)
86
+ })
87
+
88
+ onUnmounted(() => {
89
+ if (chartInstance) {
90
+ chartInstance.dispose()
91
+ }
92
+ window.removeEventListener('resize', handleResize)
93
+ })
94
+
95
+ const handleResize = () => {
96
+ chartInstance?.resize()
97
+ }
98
+
99
+ return () => (
100
+ <div
101
+ ref={chartRef}
102
+ style={{ width: '100%', height: `${props.height}px` }}
103
+ ></div>
104
+ )
105
+ },
106
+ })
@@ -0,0 +1,132 @@
1
+ import { defineComponent, onMounted, ref, watch, onUnmounted } from 'vue'
2
+ import * as echarts from 'echarts'
3
+
4
+ export default defineComponent({
5
+ name: 'EchartsRankBar',
6
+ props: {
7
+ data: {
8
+ type: Array,
9
+ default: () => [],
10
+ },
11
+ max: {
12
+ type: Number,
13
+ default: 0,
14
+ },
15
+ valueFormatter: {
16
+ type: Function,
17
+ default: (v) => v,
18
+ },
19
+ height: {
20
+ type: Number,
21
+ default: 300,
22
+ },
23
+ },
24
+ setup(props) {
25
+ const chartRef = ref(null)
26
+ let chartInstance = null
27
+
28
+ const initChart = () => {
29
+ if (!chartRef.value) return
30
+ chartInstance = echarts.init(chartRef.value)
31
+ updateOption()
32
+ }
33
+
34
+ const updateOption = () => {
35
+ if (!chartInstance) return
36
+
37
+ // 为了保证顺序(排名第一在最上面),ECharts 默认 category 轴是从下往上画的(index 0 在下)。
38
+ // 所以通常需要把数据 reverse 一下,或者 yAxis.inverse = true。
39
+ // 这里 props.data 应该是已排序好的(第一名在 index 0)。
40
+ // 使用 inverse: true 让 index 0 排在顶部。
41
+
42
+ const labels = props.data.map((d) => d.name)
43
+ const values = props.data.map((d) => d.value)
44
+
45
+ // 如果没有传 max,就取数据最大值作为背景条最大值
46
+ const maxValue = props.max || (values.length > 0 ? Math.max(...values) * 1.1 : 100)
47
+
48
+ const option = {
49
+ grid: {
50
+ top: 10,
51
+ bottom: 10,
52
+ left: 10,
53
+ right: 40, // 留给 label
54
+ containLabel: true,
55
+ },
56
+ xAxis: {
57
+ type: 'value',
58
+ show: false,
59
+ max: maxValue,
60
+ },
61
+ yAxis: {
62
+ type: 'category',
63
+ data: labels,
64
+ inverse: true, // 反转,让第一个数据在顶部
65
+ axisLine: { show: false },
66
+ axisTick: { show: false },
67
+ axisLabel: {
68
+ color: '#606266',
69
+ width: 60, // 限制宽度避免太宽
70
+ overflow: 'truncate',
71
+ },
72
+ },
73
+ series: [
74
+ {
75
+ type: 'bar',
76
+ data: values,
77
+ barWidth: 12,
78
+ showBackground: true,
79
+ backgroundStyle: {
80
+ color: '#f5f7fa',
81
+ borderRadius: 6,
82
+ },
83
+ itemStyle: {
84
+ color: '#ff7d00',
85
+ borderRadius: 6,
86
+ },
87
+ label: {
88
+ show: true,
89
+ position: 'right',
90
+ formatter: (params) => {
91
+ return props.valueFormatter(params.value)
92
+ },
93
+ color: '#333',
94
+ },
95
+ },
96
+ ],
97
+ }
98
+ chartInstance.setOption(option)
99
+ }
100
+
101
+ watch(
102
+ () => [props.data, props.max],
103
+ () => {
104
+ updateOption()
105
+ },
106
+ { deep: true }
107
+ )
108
+
109
+ onMounted(() => {
110
+ initChart()
111
+ window.addEventListener('resize', handleResize)
112
+ })
113
+
114
+ onUnmounted(() => {
115
+ if (chartInstance) {
116
+ chartInstance.dispose()
117
+ }
118
+ window.removeEventListener('resize', handleResize)
119
+ })
120
+
121
+ const handleResize = () => {
122
+ chartInstance?.resize()
123
+ }
124
+
125
+ return () => (
126
+ <div
127
+ ref={chartRef}
128
+ style={{ width: '100%', height: `${props.height}px` }}
129
+ ></div>
130
+ )
131
+ },
132
+ })
@@ -0,0 +1,176 @@
1
+ import { defineComponent, ref } from 'vue'
2
+ import styles from './index.module.scss'
3
+ import StatCardsRow from './components/StatCardsRow'
4
+ import ChartsPanel from './components/ChartsPanel'
5
+ import PracticeTable from './components/PracticeTable'
6
+ import ScriptStatsPanel from './components/ScriptStatsPanel'
7
+
8
+ /**
9
+ * 练习情况页面(按截图搭建)
10
+ * 1) 组件化拆分:StatCardsRow / ChartsPanel / PracticeTable / ScriptStatsPanel
11
+ * 2) 模拟数据:统一在父组件准备,props 传入子组件使用
12
+ * 3) 所有组件均放在 practiceStatus 文件夹内(含子目录)
13
+ * 4) JSX + Element Plus,风格对齐 aiCoach 目录现有写法
14
+ */
15
+ export default defineComponent({
16
+ // 练习情况
17
+ name: 'PracticeStatus',
18
+ setup() {
19
+ // 顶部 tabs(截图:计划统计/剧本统计)
20
+ const activeTab = ref('plan')
21
+ const useEcharts = ref(false)
22
+ setTimeout(() => useEcharts.value = true, 1000)
23
+
24
+ // 顶部筛选(截图:所属部门/职务)
25
+ const filters = ref({
26
+ dept: '全部部门',
27
+ role: '全部职务',
28
+ })
29
+
30
+ // 模拟:练习数据概览(截图 6 个卡片)
31
+ const overviewCards = ref([
32
+ { key: 'students', label: '总学员人数', value: '828' },
33
+ { key: 'rate', label: '完成率', value: '83.0%' },
34
+ { key: 'avgCount', label: '平均练练次数', value: '1.9' },
35
+ { key: 'avgDuration', label: '平均练练时长(分钟)', value: '10.1' },
36
+ { key: 'firstAvg', label: '首次练习均分', value: '56.6' },
37
+ { key: 'bestAvg', label: '最高一次均分', value: '83.4' },
38
+ ])
39
+
40
+ // 模拟:进度统计(截图:已完成/未开始/练习中)
41
+ const progressSeries = ref([
42
+ { name: '已完成', value: 68, color: '#ff7a00' },
43
+ { name: '未开始', value: 6, color: '#7bdcf6' },
44
+ { name: '练习中', value: 26, color: '#ffd08a' },
45
+ ])
46
+
47
+ // 模拟:学员练习时长排名(分钟)
48
+ const durationRank = ref([
49
+ { name: '张三', value: 469.7 },
50
+ { name: '李四', value: 270.1 },
51
+ { name: '王五', value: 75.1 },
52
+ { name: '赵六', value: 74.8 },
53
+ { name: '周七', value: 67.8 },
54
+ { name: '吴八', value: 64.0 },
55
+ { name: '郑九', value: 64.0 },
56
+ { name: '钱十', value: 64.0 },
57
+ ])
58
+
59
+ // 模拟:学员最高一次均分排名(0-100)
60
+ const scoreRank = ref([
61
+ { name: '张三', value: 100 },
62
+ { name: '李四', value: 100 },
63
+ { name: '王五', value: 100 },
64
+ { name: '赵六', value: 100 },
65
+ { name: '周七', value: 98 },
66
+ { name: '吴八', value: 96 },
67
+ { name: '郑九', value: 90 },
68
+ { name: '钱十', value: 88 },
69
+ ])
70
+
71
+ // 模拟:明细数据(截图表格)
72
+ const tableData = ref(
73
+ Array.from({ length: 30 }).map((_, idx) => {
74
+ const base = idx % 10
75
+ const score = [80, 70, 75, 90, 96, 56, 78, 67, 69, 99][base]
76
+ const progress = ['2/5', '5/5', '5/5', '5/5', '5/5', '5/5', '5/5', '5/5', '2/5', '2/5'][base]
77
+ const passStatus = score >= 60 ? '通过' : '未通过'
78
+ return {
79
+ id: `${Date.now()}-${idx}`,
80
+ jobNo: '123456',
81
+ name: ['张三', '李四', '王五', '赵六', '周七', '吴八'][idx % 6],
82
+ planName: '标准内容',
83
+ scriptName: '标准内容',
84
+ startTime: '2025-08-01',
85
+ endTime: '2025-09-01',
86
+ status: '已完成',
87
+ score,
88
+ passStatus,
89
+ progress,
90
+ }
91
+ }),
92
+ )
93
+
94
+ return () => {
95
+ return <div>
96
+ <div class={styles.practiceStatus}>
97
+ <div class={styles.toolbar}>
98
+ <div class={styles.toolbarLeft}>
99
+ <div class={styles.tabsWrap}>
100
+ <div
101
+ class={[styles.tabBtn, activeTab.value === 'plan' ? styles.tabBtnActive : null]}
102
+ onClick={() => (activeTab.value = 'plan')}
103
+ >
104
+ 计划统计
105
+ </div>
106
+ <div
107
+ class={[styles.tabBtn, activeTab.value === 'script' ? styles.tabBtnActive : null]}
108
+ onClick={() => (activeTab.value = 'script')}
109
+ >
110
+ 剧本统计
111
+ </div>
112
+ </div>
113
+ </div>
114
+ {activeTab.value === 'plan' && (
115
+ <div class={styles.toolbarRight}>
116
+ <el-switch
117
+ v-model={useEcharts.value}
118
+ active-text="ECharts"
119
+ inactive-text="CSS"
120
+ style={{ marginRight: '16px' }}
121
+ />
122
+ <el-select size="small" style={{ width: '120px' }} v-model={filters.value.dept}>
123
+ <el-option label="全部部门" value="全部部门" />
124
+ <el-option label="销售一部" value="销售一部" />
125
+ <el-option label="销售二部" value="销售二部" />
126
+ </el-select>
127
+ <el-select size="small" style={{ width: '120px' }} v-model={filters.value.role}>
128
+ <el-option label="全部职务" value="全部职务" />
129
+ <el-option label="导购" value="导购" />
130
+ <el-option label="店长" value="店长" />
131
+ </el-select>
132
+ <el-button size="small">下载</el-button>
133
+ </div>
134
+ )}
135
+ {activeTab.value === 'script' && (
136
+ <div class={styles.toolbarRight}>
137
+ <el-switch
138
+ v-model={useEcharts.value}
139
+ active-text="ECharts"
140
+ inactive-text="CSS"
141
+ style={{ marginRight: '16px' }}
142
+ />
143
+ </div>
144
+ )}
145
+ </div>
146
+
147
+ {activeTab.value === 'plan' ? (
148
+ <>
149
+ <div class={styles.section}>
150
+ <div class={styles.sectionHeader}>练习数据概览</div>
151
+ <StatCardsRow cards={overviewCards.value} />
152
+ </div>
153
+
154
+ <div class={styles.section}>
155
+ <div class={styles.sectionHeader}>练习情况统计</div>
156
+ <ChartsPanel
157
+ progress={progressSeries.value}
158
+ durationRank={durationRank.value}
159
+ scoreRank={scoreRank.value}
160
+ useEcharts={useEcharts.value}
161
+ />
162
+ </div>
163
+
164
+ <div class={styles.section}>
165
+ <div class={styles.sectionHeader}>明细数据</div>
166
+ <PracticeTable data={tableData.value} pageSize={10} />
167
+ </div>
168
+ </>
169
+ ) : (
170
+ <ScriptStatsPanel useEcharts={useEcharts.value} />
171
+ )}
172
+ </div>
173
+ </div>
174
+ }
175
+ },
176
+ })
@@ -0,0 +1,96 @@
1
+ .practiceStatus {
2
+ width: 100%;
3
+ max-width: 1100px;
4
+ }
5
+
6
+ .toolbar {
7
+ display: flex;
8
+ justify-content: space-between;
9
+ align-items: center;
10
+ margin-bottom: 12px;
11
+ }
12
+
13
+ .toolbarLeft {
14
+ display: flex;
15
+ align-items: center;
16
+ gap: 8px;
17
+ }
18
+
19
+ .toolbarTitle {
20
+ display: flex;
21
+ align-items: center;
22
+ font-size: 16px;
23
+ font-weight: 600;
24
+ color: #303133;
25
+ }
26
+
27
+ .toolbarTitle:before {
28
+ content: '';
29
+ display: inline-block;
30
+ width: 3px;
31
+ height: 14px;
32
+ background: #ff7a00;
33
+ border-radius: 2px;
34
+ margin-right: 8px;
35
+ }
36
+
37
+ .toolbarRight {
38
+ display: flex;
39
+ align-items: center;
40
+ gap: 10px;
41
+ }
42
+
43
+ .section {
44
+ margin-top: 12px;
45
+ }
46
+
47
+ .sectionHeader {
48
+ display: flex;
49
+ align-items: center;
50
+ margin-bottom: 10px;
51
+ font-size: 14px;
52
+ font-weight: 600;
53
+ color: #303133;
54
+ }
55
+
56
+ .sectionHeader:before {
57
+ content: '';
58
+ display: inline-block;
59
+ width: 3px;
60
+ height: 14px;
61
+ background: #ff7a00;
62
+ border-radius: 2px;
63
+ margin-right: 8px;
64
+ }
65
+
66
+ /* Tabs */
67
+ .tabsWrap {
68
+ display: flex;
69
+ gap: 8px;
70
+ }
71
+
72
+ .tabBtn {
73
+ padding: 6px 16px;
74
+ border: 1px solid #dcdfe6;
75
+ background: #fff;
76
+ border-radius: 4px;
77
+ color: #606266;
78
+ cursor: pointer;
79
+ font-size: 14px;
80
+ transition: all 0.2s;
81
+ }
82
+
83
+ .tabBtn:hover {
84
+ color: #ff7a00;
85
+ border-color: #ff7a00;
86
+ }
87
+
88
+ .tabBtnActive {
89
+ background: #ff7a00;
90
+ border-color: #ff7a00;
91
+ color: #fff;
92
+ }
93
+
94
+ .tabBtnActive:hover {
95
+ color: #fff;
96
+ }