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,104 @@
1
+ import { defineComponent, computed } from 'vue'
2
+
3
+ /**
4
+ * 轻量环形图(不依赖图表库)
5
+ * - 使用 SVG stroke-dasharray 实现
6
+ * - 适合做“进度统计”等概览图
7
+ */
8
+ export default defineComponent({
9
+ name: 'PracticeStatusDonutChart',
10
+ props: {
11
+ series: {
12
+ type: Array,
13
+ default: () => [],
14
+ },
15
+ size: {
16
+ type: Number,
17
+ default: 170,
18
+ },
19
+ thickness: {
20
+ type: Number,
21
+ default: 18,
22
+ },
23
+ },
24
+ setup(props) {
25
+ const normalized = computed(() => {
26
+ const safe = (props.series || []).map((s) => ({
27
+ name: s?.name ?? '',
28
+ value: Number(s?.value ?? 0),
29
+ color: s?.color ?? '#ff7a00',
30
+ }))
31
+ const total = safe.reduce((sum, cur) => sum + (Number.isFinite(cur.value) ? cur.value : 0), 0)
32
+ if (total <= 0) {
33
+ return {
34
+ total: 0,
35
+ list: safe.map((s) => ({ ...s, percent: 0 })),
36
+ }
37
+ }
38
+ return {
39
+ total,
40
+ list: safe.map((s) => ({ ...s, percent: s.value / total })),
41
+ }
42
+ })
43
+
44
+ const geometry = computed(() => {
45
+ const size = props.size
46
+ const thickness = props.thickness
47
+ const r = (size - thickness) / 2
48
+ const c = 2 * Math.PI * r
49
+ return { size, thickness, r, c }
50
+ })
51
+
52
+ const segments = computed(() => {
53
+ const { c } = geometry.value
54
+ let offset = 0
55
+ const list = normalized.value.list.map((s) => {
56
+ const len = c * s.percent
57
+ const seg = {
58
+ ...s,
59
+ len,
60
+ offset,
61
+ dasharray: `${len} ${Math.max(0, c - len)}`,
62
+ dashoffset: -offset,
63
+ }
64
+ offset += len
65
+ return seg
66
+ })
67
+ return list
68
+ })
69
+
70
+ return () => {
71
+ const { size, thickness, r } = geometry.value
72
+ const cx = size / 2
73
+ const cy = size / 2
74
+ return (
75
+ <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
76
+ <circle
77
+ cx={cx}
78
+ cy={cy}
79
+ r={r}
80
+ fill="none"
81
+ stroke="#f2f6fc"
82
+ stroke-width={thickness}
83
+ />
84
+ {segments.value.map((s, idx) => (
85
+ <circle
86
+ key={`${s.name}-${idx}`}
87
+ cx={cx}
88
+ cy={cy}
89
+ r={r}
90
+ fill="none"
91
+ stroke={s.color}
92
+ stroke-width={thickness}
93
+ stroke-linecap="round"
94
+ stroke-dasharray={s.dasharray}
95
+ stroke-dashoffset={s.dashoffset}
96
+ transform={`rotate(-90 ${cx} ${cy})`}
97
+ />
98
+ ))}
99
+ </svg>
100
+ )
101
+ }
102
+ },
103
+ })
104
+
@@ -0,0 +1,75 @@
1
+ import { defineComponent, ref, computed } from 'vue'
2
+ import styles from './index.module.scss'
3
+
4
+ /**
5
+ * 明细数据表格
6
+ * - 组件化目的:后续切换接口数据时,只替换 props.data
7
+ * - 目前仅实现展示与分页(模拟分页)
8
+ */
9
+ export default defineComponent({
10
+ name: 'PracticeStatusPracticeTable',
11
+ props: {
12
+ data: {
13
+ type: Array,
14
+ default: () => [],
15
+ },
16
+ pageSize: {
17
+ type: Number,
18
+ default: 10,
19
+ },
20
+ },
21
+ setup(props) {
22
+ const currentPage = ref(1)
23
+
24
+ const total = computed(() => props.data.length)
25
+ const pageCount = computed(() => Math.max(1, Math.ceil(total.value / props.pageSize)))
26
+
27
+ const pageData = computed(() => {
28
+ const start = (currentPage.value - 1) * props.pageSize
29
+ return props.data.slice(start, start + props.pageSize)
30
+ })
31
+
32
+ const handlePageChange = (p) => {
33
+ currentPage.value = p
34
+ }
35
+
36
+ return () => (
37
+ <div class={styles.tableWrap}>
38
+ <el-table data={pageData.value} stripe style={{ width: '100%' }}>
39
+ <el-table-column type="selection" width="44" />
40
+ <el-table-column prop="jobNo" label="工号" width="90" />
41
+ <el-table-column prop="name" label="姓名" width="80" />
42
+ <el-table-column prop="planName" label="计划名称" />
43
+ <el-table-column prop="scriptName" label="脚本名称" />
44
+ <el-table-column prop="startTime" label="开始时间" width="120" />
45
+ <el-table-column prop="endTime" label="结束时间" width="120" />
46
+ <el-table-column prop="status" label="完成状态" width="90" />
47
+ <el-table-column prop="score" label="得分" width="70" />
48
+ <el-table-column prop="passStatus" label="通过状态" width="90" />
49
+ <el-table-column prop="progress" label="练习进度" width="90" />
50
+ <el-table-column label="操作" width="110">
51
+ {{
52
+ default: (scope) => (
53
+ <el-button link size="small" type="primary" onClick={() => console.log('查看练习记录', scope.row)}>
54
+ 查看练习记录
55
+ </el-button>
56
+ ),
57
+ }}
58
+ </el-table-column>
59
+ </el-table>
60
+
61
+ <div class={styles.paginationRow}>
62
+ <el-pagination
63
+ background
64
+ currentPage={currentPage.value}
65
+ pageSize={props.pageSize}
66
+ total={total.value}
67
+ layout="total, prev, pager, next"
68
+ onCurrentChange={handlePageChange}
69
+ />
70
+ </div>
71
+ </div>
72
+ )
73
+ },
74
+ })
75
+
@@ -0,0 +1,12 @@
1
+ .tableWrap {
2
+ background: #fff;
3
+ border-radius: 8px;
4
+ padding: 12px;
5
+ border: 1px solid #ebeef5;
6
+ }
7
+
8
+ .paginationRow {
9
+ display: flex;
10
+ justify-content: flex-end;
11
+ margin-top: 12px;
12
+ }
@@ -0,0 +1,62 @@
1
+ import { defineComponent, computed } from 'vue'
2
+ import styles from './index.module.scss'
3
+
4
+ /**
5
+ * 横向条形排行(不依赖图表库)
6
+ * - 通过百分比宽度绘制
7
+ * - 支持自定义最大值,便于复用
8
+ */
9
+ export default defineComponent({
10
+ name: 'PracticeStatusRankBarChart',
11
+ props: {
12
+ data: {
13
+ type: Array,
14
+ default: () => [],
15
+ },
16
+ max: {
17
+ type: Number,
18
+ default: undefined,
19
+ },
20
+ valueFormatter: {
21
+ type: Function,
22
+ default: (v) => `${v}`,
23
+ },
24
+ },
25
+ setup(props) {
26
+ const maxValue = computed(() => {
27
+ const list = props.data || []
28
+ const maxInData = list.reduce((m, cur) => Math.max(m, Number(cur?.value ?? 0)), 0)
29
+ const base = Number.isFinite(props.max) ? props.max : maxInData
30
+ return base > 0 ? base : 1
31
+ })
32
+
33
+ const rows = computed(() =>
34
+ (props.data || []).map((it) => {
35
+ const value = Number(it?.value ?? 0)
36
+ const ratio = Math.min(1, Math.max(0, value / maxValue.value))
37
+ return {
38
+ name: it?.name ?? '',
39
+ value,
40
+ ratio,
41
+ }
42
+ }),
43
+ )
44
+
45
+ return () => (
46
+ <div class={styles.barsWrap}>
47
+ {rows.value.map((row, idx) => (
48
+ <div class={styles.barRow} key={`${row.name}-${idx}`}>
49
+ <div class={styles.barName} title={row.name}>
50
+ {row.name}
51
+ </div>
52
+ <div class={styles.barTrack}>
53
+ <div class={styles.barFill} style={{ width: `${row.ratio * 100}%` }} />
54
+ </div>
55
+ <div class={styles.barValue}>{props.valueFormatter(row.value)}</div>
56
+ </div>
57
+ ))}
58
+ </div>
59
+ )
60
+ },
61
+ })
62
+
@@ -0,0 +1,43 @@
1
+ .barsWrap {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 8px;
5
+ }
6
+
7
+ .barRow {
8
+ display: flex;
9
+ align-items: center;
10
+ height: 24px;
11
+ }
12
+
13
+ .barName {
14
+ width: 60px;
15
+ font-size: 12px;
16
+ color: #606266;
17
+ white-space: nowrap;
18
+ overflow: hidden;
19
+ text-overflow: ellipsis;
20
+ margin-right: 8px;
21
+ text-align: right;
22
+ }
23
+
24
+ .barTrack {
25
+ flex: 1;
26
+ height: 12px;
27
+ background: #f5f7fa;
28
+ border-radius: 6px;
29
+ overflow: hidden;
30
+ margin-right: 8px;
31
+ }
32
+
33
+ .barFill {
34
+ height: 100%;
35
+ background: #ff7d00;
36
+ border-radius: 6px;
37
+ }
38
+
39
+ .barValue {
40
+ width: 40px;
41
+ font-size: 12px;
42
+ color: #333;
43
+ }
@@ -0,0 +1,29 @@
1
+ import { defineComponent } from 'vue'
2
+ import RankingList from '../RankingList'
3
+ import styles from './index.module.scss'
4
+
5
+ /**
6
+ * 剧本统计 - 排行榜组(三列布局)
7
+ */
8
+ export default defineComponent({
9
+ name: 'RankingGroup',
10
+ props: {
11
+ data: {
12
+ type: Object,
13
+ default: () => ({
14
+ overall: [],
15
+ accuracy: [],
16
+ fluency: [],
17
+ }),
18
+ },
19
+ },
20
+ setup(props) {
21
+ return () => (
22
+ <div class={styles.rankingGroup}>
23
+ <RankingList title="综合排行榜" data={props.data.overall} />
24
+ <RankingList title="话术准确度" data={props.data.accuracy} />
25
+ <RankingList title="流利度" data={props.data.fluency} />
26
+ </div>
27
+ )
28
+ },
29
+ })
@@ -0,0 +1,5 @@
1
+ .rankingGroup {
2
+ display: grid;
3
+ grid-template-columns: repeat(3, 1fr);
4
+ gap: 12px;
5
+ }
@@ -0,0 +1,58 @@
1
+ import { defineComponent, toRef } from 'vue'
2
+ import styles from './index.module.scss'
3
+
4
+ /**
5
+ * 剧本统计 - 排行榜列表项
6
+ * 用于展示单列排行榜(综合、准确度、流利度等)
7
+ */
8
+ export default defineComponent({
9
+ name: 'RankingList',
10
+ props: {
11
+ title: {
12
+ type: String,
13
+ default: '',
14
+ },
15
+ data: {
16
+ type: Array,
17
+ default: () => [],
18
+ },
19
+ },
20
+ setup(props) {
21
+ const list = toRef(props, 'data')
22
+
23
+ // 奖牌图标(模拟 SVG 或使用 emoji/图片)
24
+ const getRankIcon = (index) => {
25
+ if (index === 0) return '🥇'
26
+ if (index === 1) return '🥈'
27
+ if (index === 2) return '🥉'
28
+ return index + 1
29
+ }
30
+
31
+ const getRankClass = (index) => {
32
+ if (index === 0) return styles.rankFirst
33
+ if (index === 1) return styles.rankSecond
34
+ if (index === 2) return styles.rankThird
35
+ return styles.rankNormal
36
+ }
37
+
38
+ return () => (
39
+ <div class={styles.rankingCard}>
40
+ <div class={styles.rankingTitle}>{props.title}</div>
41
+ <div class={styles.rankingList}>
42
+ {list.value.map((item, index) => (
43
+ <div key={index} class={styles.rankingItem}>
44
+ <div class={[styles.rankIndex, getRankClass(index)]}>
45
+ {getRankIcon(index)}
46
+ </div>
47
+ <div class={styles.rankInfo}>
48
+ <div class={styles.rankName}>{item.name}</div>
49
+ <div class={styles.rankDept}>{item.dept}</div>
50
+ </div>
51
+ <div class={styles.rankScore}>{item.score}</div>
52
+ </div>
53
+ ))}
54
+ </div>
55
+ </div>
56
+ )
57
+ },
58
+ })
@@ -0,0 +1,85 @@
1
+ .rankingCard {
2
+ background: #fff;
3
+ border-radius: 8px;
4
+ padding: 16px;
5
+ border: 1px solid #ebeef5;
6
+ }
7
+
8
+ .rankingTitle {
9
+ font-size: 14px;
10
+ font-weight: 600;
11
+ color: #303133;
12
+ margin-bottom: 12px;
13
+ }
14
+
15
+ .rankingList {
16
+ display: flex;
17
+ flex-direction: column;
18
+ gap: 8px;
19
+ }
20
+
21
+ .rankingItem {
22
+ display: flex;
23
+ align-items: center;
24
+ padding: 8px 0;
25
+ border-bottom: 1px dashed #ebeef5;
26
+ }
27
+
28
+ .rankingItem:last-child {
29
+ border-bottom: none;
30
+ }
31
+
32
+ .rankIndex {
33
+ width: 24px;
34
+ height: 24px;
35
+ display: flex;
36
+ align-items: center;
37
+ justify-content: center;
38
+ font-size: 14px;
39
+ color: #909399;
40
+ font-weight: 600;
41
+ margin-right: 12px;
42
+ }
43
+
44
+ .rankFirst {
45
+ color: #ff7d00;
46
+ font-size: 20px;
47
+ }
48
+
49
+ .rankSecond {
50
+ color: #909399;
51
+ font-size: 20px;
52
+ }
53
+
54
+ .rankThird {
55
+ color: #bfa176;
56
+ font-size: 20px;
57
+ }
58
+
59
+ .rankNormal {
60
+ color: #606266;
61
+ }
62
+
63
+ .rankInfo {
64
+ flex: 1;
65
+ display: flex;
66
+ flex-direction: column;
67
+ }
68
+
69
+ .rankName {
70
+ font-size: 14px;
71
+ color: #303133;
72
+ font-weight: 500;
73
+ }
74
+
75
+ .rankDept {
76
+ font-size: 12px;
77
+ color: #909399;
78
+ margin-top: 2px;
79
+ }
80
+
81
+ .rankScore {
82
+ font-size: 16px;
83
+ font-weight: 600;
84
+ color: #303133;
85
+ }
@@ -0,0 +1,92 @@
1
+ import { defineComponent, ref } from 'vue'
2
+ import styles from './index.module.scss'
3
+ import EchartsRankBar from '../echarts/EchartsRankBar'
4
+ import RankBarChart from '../RankBarChart'
5
+ import RankingGroup from '../RankingGroup'
6
+
7
+ /**
8
+ * 剧本统计主面板
9
+ */
10
+ export default defineComponent({
11
+ name: 'ScriptStatsPanel',
12
+ props: {
13
+ useEcharts: {
14
+ type: Boolean,
15
+ default: false,
16
+ },
17
+ },
18
+ setup(props) {
19
+ // 模拟数据
20
+ const dimensionData = ref([
21
+ { name: '语言组织', value: 95.4, max: 100 },
22
+ { name: '共情与优势介绍', value: 75.2, max: 100 },
23
+ { name: '流利度', value: 72.5, max: 100 },
24
+ { name: '顾客打消能力', value: 58.7, max: 100 },
25
+ { name: '后续练联系告知', value: 52.7, max: 100 },
26
+ { name: '合规话术', value: 0.0, max: 100 },
27
+ ])
28
+
29
+ const generateRanking = (count) => {
30
+ return Array.from({ length: count }).map((_, i) => ({
31
+ name: '李四',
32
+ dept: '渠道营销三部',
33
+ score: 100,
34
+ }))
35
+ }
36
+
37
+ const rankingData = ref({
38
+ overall: generateRanking(10),
39
+ accuracy: generateRanking(10),
40
+ fluency: generateRanking(10),
41
+ })
42
+
43
+ const handleClear = () => {
44
+ console.log('清空已选')
45
+ }
46
+
47
+ const handleDownload = () => {
48
+ console.log('下载')
49
+ }
50
+
51
+ return () => (
52
+ <div class={styles.scriptStatsPanel}>
53
+ {/* 顶部筛选栏 */}
54
+ <div class={styles.filterBar}>
55
+ <div class={styles.filterLeft}>
56
+ {/* 这里的按钮已经在 index.jsx 的 toggle 控制,这里主要放筛选 */}
57
+ </div>
58
+ <div class={styles.filterRight}>
59
+ <el-button link type="primary" onClick={handleClear} style={{ color: '#ff7a00' }}>
60
+ 清空已选
61
+ </el-button>
62
+ <el-select placeholder="剧本名称: 电销话术1V1模拟训练" style={{ width: '240px' }} modelValue="">
63
+ <el-option label="电销话术1V1模拟训练" value="1" />
64
+ </el-select>
65
+ <el-select placeholder="所属部门" style={{ width: '120px' }} modelValue="">
66
+ <el-option label="部门A" value="A" />
67
+ </el-select>
68
+ <el-button onClick={handleDownload}>下载</el-button>
69
+ </div>
70
+ </div>
71
+
72
+ {/* 维度得分排名 */}
73
+ <div class={styles.section}>
74
+ <div class={styles.sectionHeader}>维度得分排名</div>
75
+ <div class={styles.chartContainer}>
76
+ {props.useEcharts ? (
77
+ <EchartsRankBar data={dimensionData.value} height={300} max={100} />
78
+ ) : (
79
+ <RankBarChart data={dimensionData.value} height={300} />
80
+ )}
81
+ </div>
82
+ </div>
83
+
84
+ {/* 排行榜 */}
85
+ <div class={styles.section}>
86
+ <div class={styles.sectionHeader}>排行榜</div>
87
+ <RankingGroup data={rankingData.value} />
88
+ </div>
89
+ </div>
90
+ )
91
+ },
92
+ })
@@ -0,0 +1,56 @@
1
+ .scriptStatsPanel {
2
+ /* 容器样式 */
3
+ }
4
+
5
+ .filterBar {
6
+ display: flex;
7
+ justify-content: space-between;
8
+ align-items: center;
9
+ background: #fff;
10
+ padding: 12px;
11
+ border-radius: 8px;
12
+ border: 1px solid #ebeef5;
13
+ margin-bottom: 12px;
14
+ }
15
+
16
+ .filterLeft {
17
+ display: flex;
18
+ align-items: center;
19
+ gap: 8px;
20
+ }
21
+
22
+ .filterRight {
23
+ display: flex;
24
+ align-items: center;
25
+ gap: 12px;
26
+ }
27
+
28
+ .chartContainer {
29
+ background: #fff;
30
+ padding: 16px;
31
+ border-radius: 8px;
32
+ border: 1px solid #ebeef5;
33
+ }
34
+
35
+ .section {
36
+ margin-top: 12px;
37
+ }
38
+
39
+ .sectionHeader {
40
+ display: flex;
41
+ align-items: center;
42
+ margin-bottom: 10px;
43
+ font-size: 14px;
44
+ font-weight: 600;
45
+ color: #303133;
46
+ }
47
+
48
+ .sectionHeader:before {
49
+ content: '';
50
+ display: inline-block;
51
+ width: 3px;
52
+ height: 14px;
53
+ background: #ff7a00;
54
+ border-radius: 2px;
55
+ margin-right: 8px;
56
+ }
@@ -0,0 +1,40 @@
1
+ import { defineComponent } from 'vue'
2
+ import styles from './index.module.scss'
3
+
4
+ /**
5
+ * 练习数据概览卡片行
6
+ * - 组件化目的:后续可复用在其他统计页
7
+ * - 数据由父组件通过 props 传入
8
+ */
9
+ export default defineComponent({
10
+ name: 'PracticeStatusStatCardsRow',
11
+ props: {
12
+ cards: {
13
+ type: Array,
14
+ default: () => [],
15
+ },
16
+ },
17
+ setup(props) {
18
+ const bgClassList = [
19
+ styles.cardBg1,
20
+ styles.cardBg2,
21
+ styles.cardBg3,
22
+ styles.cardBg4,
23
+ styles.cardBg5,
24
+ styles.cardBg6,
25
+ ]
26
+
27
+ return () => (
28
+ <div class={styles.cardsRow}>
29
+ {props.cards.map((card, idx) => (
30
+ <div class={[styles.statCard, bgClassList[idx % bgClassList.length]]} key={card.key || idx}>
31
+ <div class={styles.statCardLabel}>{card.label}</div>
32
+ <div class={styles.statCardValue}>{card.value}</div>
33
+ {card.hint ? <div class={styles.statCardHint}>{card.hint}</div> : null}
34
+ </div>
35
+ ))}
36
+ </div>
37
+ )
38
+ },
39
+ })
40
+