statistics.vue 23 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057
  1. <template>
  2. <view class="container">
  3. <!-- 统计类型切换 -->
  4. <view class="stat-tabs">
  5. <view
  6. class="stat-tab"
  7. :class="{ active: currentStatType === 'task' }"
  8. @click="switchStatType('task')"
  9. >
  10. 任务统计
  11. </view>
  12. <view
  13. class="stat-tab"
  14. :class="{ active: currentStatType === 'service' }"
  15. @click="switchStatType('service')"
  16. >
  17. 服务单统计
  18. </view>
  19. </view>
  20. <!-- 日期范围和筛选 -->
  21. <view class="date-filter-bar mb16">
  22. <view class="date-range" @click="showDatePicker = true">
  23. <image class="w32 h32" src="@/static/image/icon_time.png" mode=""></image>
  24. <text class="date-text">{{ dateRangeText }}</text>
  25. <text class="arrow-down">▼</text>
  26. </view>
  27. <view class="filter-btn" @click="showFilter = true">
  28. <image class="w32 h32" src="@/static/image/icon_select.png" mode=""></image>
  29. <text>筛选</text>
  30. </view>
  31. </view>
  32. <!-- 内容区域 - 动态显示不同的统计表格 -->
  33. <template v-if="currentStatType === 'task'">
  34. <!-- 任务完成统计表格 -->
  35. <statistics-table
  36. :summary-title="summaryTitle.task[0]"
  37. :summary-stats="summaryStats.taskComplete"
  38. :columns="tableColumns.task"
  39. :table-data="tableData.taskComplete"
  40. :loading="loading.task"
  41. @load-more="loadMore('task')"
  42. >
  43. </statistics-table>
  44. <!-- 任务创建统计表格 -->
  45. <statistics-table
  46. :summary-title="summaryTitle.task[1]"
  47. :summary-stats="summaryStats.taskCreate"
  48. :columns="tableColumns.task"
  49. :table-data="tableData.taskCreate"
  50. :loading="loading.task"
  51. @load-more="loadMore('task')"
  52. >
  53. </statistics-table>
  54. </template>
  55. <template v-else-if="currentStatType === 'service'">
  56. <!-- 服务单统计表格 -->
  57. <statistics-table
  58. :summary-title="summaryTitle.service[0]"
  59. :summary-stats="summaryStats.service"
  60. :columns="tableColumns.service"
  61. :table-data="tableData.service"
  62. :loading="loading.service"
  63. @load-more="loadMore('service')"
  64. >
  65. <!-- 状态列自定义插槽 -->
  66. <template #statusText="{ item }">
  67. <text class="status-tag" :class="item.status">{{ item.statusText }}</text>
  68. </template>
  69. </statistics-table>
  70. <statistics-table
  71. :summary-title="summaryTitle.service[1]"
  72. :summary-stats="summaryStats.service"
  73. :columns="tableColumns.service"
  74. :table-data="tableData.service"
  75. :loading="loading.service"
  76. @load-more="loadMore('service')"
  77. >
  78. <!-- 状态列自定义插槽 -->
  79. <template #statusText="{ item }">
  80. <text class="status-tag" :class="item.status">{{ item.statusText }}</text>
  81. </template>
  82. </statistics-table>
  83. </template>
  84. <!-- 日期选择弹窗 -->
  85. <view class="date-picker-popup" v-if="showDatePicker" @click="showDatePicker = false">
  86. <view class="date-picker-content" @click.stop>
  87. <view class="picker-header">
  88. <view class="picker-cancel" @click="showDatePicker = false">取消</view>
  89. <view class="picker-title">选择日期范围</view>
  90. <view class="picker-confirm" @click="confirmDateRange">确定</view>
  91. </view>
  92. <view class="picker-body">
  93. <view class="date-item">
  94. <text class="date-label">开始日期</text>
  95. <picker mode="date" :value="tempDateRange.startDate" @change="onStartDateChange">
  96. <view class="date-value">{{ tempDateRange.startDate || '请选择' }}</view>
  97. </picker>
  98. </view>
  99. <view class="date-item">
  100. <text class="date-label">结束日期</text>
  101. <picker mode="date" :value="tempDateRange.endDate" @change="onEndDateChange">
  102. <view class="date-value">{{ tempDateRange.endDate || '请选择' }}</view>
  103. </picker>
  104. </view>
  105. </view>
  106. </view>
  107. </view>
  108. <!-- 筛选弹窗 -->
  109. <view class="filter-popup" v-if="showFilter" @click="closeFilter">
  110. <view class="filter-content" @click.stop>
  111. <view class="filter-header">
  112. <view class="filter-title">筛选</view>
  113. <image class="filter-close-btn" src="/static/image/icon_cross.png" @click="closeFilter"></image>
  114. </view>
  115. <!-- 根据统计类型显示不同的筛选条件 -->
  116. <template v-if="currentStatType === 'task'">
  117. <!-- 任务类型筛选 -->
  118. <view class="filter-group">
  119. <view class="group-label">任务类型</view>
  120. <view class="filter-tags">
  121. <view class="filter-tag" :class="{ active: tempSelectedTaskType === item.value }"
  122. v-for="(item, index) in taskTypeOptions" :key="index" @click="selectTaskType(item.value)">
  123. {{ item.label }}
  124. </view>
  125. </view>
  126. </view>
  127. <!-- 任务状态筛选 -->
  128. <view class="filter-group">
  129. <view class="group-label">任务状态</view>
  130. <view class="filter-tags">
  131. <view class="filter-tag" :class="{ active: tempSelectedTaskStatus === item.value }"
  132. v-for="(item, index) in taskStatusOptions" :key="index"
  133. @click="selectTaskStatus(item.value)">
  134. {{ item.label }}
  135. </view>
  136. </view>
  137. </view>
  138. </template>
  139. <template v-else-if="currentStatType === 'service'">
  140. <!-- 服务单类型筛选 -->
  141. <view class="filter-group">
  142. <view class="group-label">服务单类型</view>
  143. <view class="filter-tags">
  144. <view class="filter-tag" :class="{ active: tempSelectedServiceType === item.value }"
  145. v-for="(item, index) in serviceTypeOptions" :key="index" @click="selectServiceType(item.value)">
  146. {{ item.label }}
  147. </view>
  148. </view>
  149. </view>
  150. <!-- 服务单状态筛选 -->
  151. <view class="filter-group">
  152. <view class="group-label">服务单状态</view>
  153. <view class="filter-tags">
  154. <view class="filter-tag" :class="{ active: tempSelectedServiceStatus === item.value }"
  155. v-for="(item, index) in serviceStatusOptions" :key="index"
  156. @click="selectServiceStatus(item.value)">
  157. {{ item.label }}
  158. </view>
  159. </view>
  160. </view>
  161. </template>
  162. <!-- 操作按钮 -->
  163. <view class="filter-actions">
  164. <view class="reset-btn" @click="resetFilters">重置</view>
  165. <view class="confirm-btn" @click="confirmFilters">确定</view>
  166. </view>
  167. </view>
  168. </view>
  169. </view>
  170. </template>
  171. <script>
  172. import StatisticsTable from '@/components/StatisticsTable.vue'
  173. import { getTaskCompleteStats, getTaskCreateStats } from '@/api/task.js'
  174. export default {
  175. components: {
  176. StatisticsTable
  177. },
  178. data() {
  179. return {
  180. statusBarHeight: uni.getSystemInfoSync().statusBarHeight + 'px',
  181. showDatePicker: false,
  182. showFilter: false,
  183. currentStatType: 'task', // 当前统计类型: task-任务统计, service-服务单统计
  184. // 日期范围
  185. dateRange: {
  186. startDate: '2025-12-01',
  187. endDate: '2025-12-25'
  188. },
  189. tempDateRange: {
  190. startDate: '2025-12-01',
  191. endDate: '2025-12-25'
  192. },
  193. // 任务统计筛选
  194. selectedTaskType: 'academicLecture',
  195. selectedTaskStatus: '',
  196. tempSelectedTaskType: 'academicLecture',
  197. tempSelectedTaskStatus: '',
  198. // 服务单统计筛选
  199. selectedServiceType: 'all',
  200. selectedServiceStatus: '',
  201. tempSelectedServiceType: 'all',
  202. tempSelectedServiceStatus: '',
  203. // 任务类型选项
  204. taskTypeOptions: [{
  205. label: '医生坐诊',
  206. value: 'doctorConsultation'
  207. },
  208. {
  209. label: '科普讲座',
  210. value: 'popularScienceLecture'
  211. },
  212. {
  213. label: '学术讲座',
  214. value: 'academicLecture'
  215. },
  216. {
  217. label: '科普文章',
  218. value: 'popularScienceArticle'
  219. },
  220. {
  221. label: '科普短视频',
  222. value: 'popularScienceShortVideo'
  223. },
  224. {
  225. label: '科普长视频',
  226. value: 'popularScienceLongVideo'
  227. },
  228. {
  229. label: '空中课堂',
  230. value: 'airClassroom'
  231. },
  232. {
  233. label: '用药调研',
  234. value: 'medicationSurvey'
  235. },
  236. {
  237. label: '问卷调研',
  238. value: 'questionnaireSurvey'
  239. },
  240. {
  241. label: '社群咨询',
  242. value: 'communityConsultation'
  243. },
  244. {
  245. label: '健康问答',
  246. value: 'healthQA'
  247. }
  248. ],
  249. // 任务状态选项
  250. taskStatusOptions: [{
  251. label: '未完成',
  252. value: 'uncompleted'
  253. },
  254. {
  255. label: '待审核',
  256. value: 'pendingReview'
  257. },
  258. {
  259. label: '已驳回',
  260. value: 'rejected'
  261. },
  262. {
  263. label: '已完成',
  264. value: 'completed'
  265. },
  266. {
  267. label: '已完结',
  268. value: 'finished'
  269. }
  270. ],
  271. // 服务单类型选项
  272. serviceTypeOptions: [{
  273. label: '全部类型',
  274. value: 'all'
  275. },
  276. {
  277. label: '技术咨询',
  278. value: 'techConsultation'
  279. },
  280. {
  281. label: '售后服务',
  282. value: 'afterSales'
  283. },
  284. {
  285. label: '安装服务',
  286. value: 'installation'
  287. },
  288. {
  289. label: '维修服务',
  290. value: 'repair'
  291. },
  292. {
  293. label: '投诉建议',
  294. value: 'complaint'
  295. }
  296. ],
  297. // 服务单状态选项
  298. serviceStatusOptions: [{
  299. label: '待处理',
  300. value: 'pending'
  301. },
  302. {
  303. label: '处理中',
  304. value: 'processing'
  305. },
  306. {
  307. label: '已完成',
  308. value: 'completed'
  309. },
  310. {
  311. label: '已关闭',
  312. value: 'closed'
  313. },
  314. {
  315. label: '已取消',
  316. value: 'cancelled'
  317. }
  318. ],
  319. // 统计标题(分开存储)
  320. summaryTitle: {
  321. service: [ '汇总统计', '明细统计'],
  322. task: ['任务完成统计', '任务创建统计']
  323. },
  324. // 统计数据
  325. summaryStats: {
  326. taskComplete: [{
  327. label: '总积分',
  328. value: 300
  329. }],
  330. taskCreate: [{
  331. label: '总积分',
  332. value: 1000
  333. }],
  334. service: [{
  335. label: '总积分',
  336. value: 45
  337. }]
  338. },
  339. // 表格列定义
  340. tableColumns: {
  341. task: [{
  342. title: '归属',
  343. key: 'deptName',
  344. width: '20%'
  345. },
  346. {
  347. title: '类型',
  348. key: 'taskType',
  349. width: '20%'
  350. },
  351. {
  352. title: '数量',
  353. key: 'count',
  354. width: '15%'
  355. },
  356. {
  357. title: '客户',
  358. key: 'companyUserName',
  359. width: '20%'
  360. },{"title": "业务员",
  361. "key": "doctorName",
  362. "width": "20%"
  363. },
  364. {
  365. title: '积分',
  366. key: 'totalPoints',
  367. width: '25%'
  368. }],
  369. service: [{
  370. title: '项目归属',
  371. key: 'serviceNo',
  372. width: '25%'
  373. },
  374. {
  375. title: '类型',
  376. key: 'serviceType',
  377. width: '20%'
  378. },
  379. {
  380. title: '数量',
  381. key: 'customerName',
  382. width: '20%'
  383. },
  384. {
  385. title: '客户',
  386. key: 'statusText',
  387. width: '20%',
  388. slot: true
  389. },
  390. {
  391. title: '业务员',
  392. key: 'createTime',
  393. width: '25%'
  394. },
  395. {
  396. title: '积分',
  397. key: 'createTime',
  398. width: '25%'
  399. }
  400. ]
  401. },
  402. // 表格数据
  403. tableData: {
  404. taskComplete: [
  405. ],
  406. taskCreate: [
  407. ],
  408. service: [{
  409. serviceNo: '学术研究',
  410. serviceType: '技术咨询',
  411. customerName: '李*华',
  412. status: 'completed',
  413. statusText: '已完成',
  414. createTime: '2025-12-25'
  415. },
  416. {
  417. serviceNo: '学术研究',
  418. serviceType: '售后服务',
  419. customerName: '张*伟',
  420. status: 'processing',
  421. statusText: '处理中',
  422. createTime: '2025-12-24'
  423. },
  424. {
  425. serviceNo: '学术研究',
  426. serviceType: '安装服务',
  427. customerName: '王*芳',
  428. status: 'pending',
  429. statusText: '待处理',
  430. createTime: '2025-12-23'
  431. }
  432. ]
  433. },
  434. // 加载状态
  435. loading: {
  436. task: false,
  437. service: false
  438. }
  439. }
  440. },
  441. computed: {
  442. dateRangeText() {
  443. return `${this.dateRange.startDate} 至 ${this.dateRange.endDate}`
  444. }
  445. },
  446. watch: {
  447. showFilter(newVal) {
  448. if (newVal) {
  449. // 打开弹窗时,同步临时选择值
  450. if (this.currentStatType === 'task') {
  451. this.tempSelectedTaskType = this.selectedTaskType
  452. this.tempSelectedTaskStatus = this.selectedTaskStatus
  453. } else {
  454. this.tempSelectedServiceType = this.selectedServiceType
  455. this.tempSelectedServiceStatus = this.selectedServiceStatus
  456. }
  457. }
  458. },
  459. currentStatType(newVal) {
  460. // 切换统计类型时,重置筛选条件(可选)
  461. this.resetFilters()
  462. // 重新加载数据
  463. this.loadData()
  464. }
  465. },
  466. onLoad() {
  467. this.loadData()
  468. },
  469. onReachBottom() {
  470. this.loadMore(this.currentStatType)
  471. },
  472. methods: {
  473. // 切换统计类型
  474. switchStatType(type) {
  475. if (this.currentStatType !== type) {
  476. this.currentStatType = type
  477. }
  478. },
  479. goBack() {
  480. uni.navigateBack()
  481. },
  482. onStartDateChange(e) {
  483. this.tempDateRange.startDate = e.detail.value
  484. },
  485. onEndDateChange(e) {
  486. this.tempDateRange.endDate = e.detail.value
  487. },
  488. confirmDateRange() {
  489. if (!this.tempDateRange.startDate || !this.tempDateRange.endDate) {
  490. uni.showToast({
  491. icon: 'none',
  492. title: '请选择完整的日期范围'
  493. })
  494. return
  495. }
  496. if (new Date(this.tempDateRange.startDate) > new Date(this.tempDateRange.endDate)) {
  497. uni.showToast({
  498. icon: 'none',
  499. title: '开始日期不能大于结束日期'
  500. })
  501. return
  502. }
  503. this.dateRange = {
  504. ...this.tempDateRange
  505. }
  506. this.showDatePicker = false
  507. this.loadData()
  508. },
  509. closeFilter() {
  510. // 关闭弹窗时,恢复临时选择值为当前选择值
  511. if (this.currentStatType === 'task') {
  512. this.tempSelectedTaskType = this.selectedTaskType
  513. this.tempSelectedTaskStatus = this.selectedTaskStatus
  514. } else {
  515. this.tempSelectedServiceType = this.selectedServiceType
  516. this.tempSelectedServiceStatus = this.selectedServiceStatus
  517. }
  518. this.showFilter = false
  519. },
  520. // 任务筛选相关方法
  521. selectTaskType(value) {
  522. if (this.tempSelectedTaskType === value) {
  523. this.tempSelectedTaskType = ''
  524. } else {
  525. this.tempSelectedTaskType = value
  526. }
  527. },
  528. selectTaskStatus(value) {
  529. if (this.tempSelectedTaskStatus === value) {
  530. this.tempSelectedTaskStatus = ''
  531. } else {
  532. this.tempSelectedTaskStatus = value
  533. }
  534. },
  535. // 服务单筛选相关方法
  536. selectServiceType(value) {
  537. if (this.tempSelectedServiceType === value) {
  538. this.tempSelectedServiceType = ''
  539. } else {
  540. this.tempSelectedServiceType = value
  541. }
  542. },
  543. selectServiceStatus(value) {
  544. if (this.tempSelectedServiceStatus === value) {
  545. this.tempSelectedServiceStatus = ''
  546. } else {
  547. this.tempSelectedServiceStatus = value
  548. }
  549. },
  550. resetFilters() {
  551. if (this.currentStatType === 'task') {
  552. this.tempSelectedTaskType = ''
  553. this.tempSelectedTaskStatus = ''
  554. } else {
  555. this.tempSelectedServiceType = 'all'
  556. this.tempSelectedServiceStatus = ''
  557. }
  558. },
  559. confirmFilters() {
  560. if (this.currentStatType === 'task') {
  561. this.selectedTaskType = this.tempSelectedTaskType
  562. this.selectedTaskStatus = this.tempSelectedTaskStatus
  563. } else {
  564. this.selectedServiceType = this.tempSelectedServiceType
  565. this.selectedServiceStatus = this.tempSelectedServiceStatus
  566. }
  567. this.showFilter = false
  568. this.loadData()
  569. },
  570. async loadData() {
  571. try {
  572. uni.showLoading({
  573. title: '加载中...'
  574. })
  575. // 根据当前统计类型加载数据
  576. if (this.currentStatType === 'task') {
  577. // 加载任务统计数据
  578. // 获取用户信息
  579. const userInfoStr = uni.getStorageSync('userInfo')
  580. const userInfo = userInfoStr ? JSON.parse(userInfoStr) : {}
  581. // 构建请求参数
  582. const params = {
  583. companyId: userInfo.companyId || 1,
  584. pageNum: 1,
  585. pageSize: 10
  586. }
  587. // 调用任务完成统计接口
  588. const completeStatsRes = await getTaskCompleteStats(params)
  589. // 调用任务创建统计接口
  590. const createStatsRes = await getTaskCreateStats(params)
  591. // 处理返回的数据
  592. if (completeStatsRes.code === 200) {
  593. // 更新任务完成统计数据
  594. console.log('任务完成统计数据:', completeStatsRes)
  595. // 更新表格数据
  596. this.tableData.taskComplete = completeStatsRes.rows.map(item => ({
  597. ...item,
  598. taskType: item.taskType || '无类型'
  599. }))
  600. // 更新汇总统计
  601. this.summaryStats.taskComplete = [{
  602. label: '总任务数',
  603. value: completeStatsRes.total || 0
  604. }, {
  605. label: '总积分',
  606. value: completeStatsRes.rows.reduce((sum, item) => sum + (item.totalPoints || 0), 0)
  607. }]
  608. }
  609. if (createStatsRes.code === 200) {
  610. // 更新任务创建统计数据
  611. console.log('任务创建统计数据:', createStatsRes)
  612. // 更新表格数据
  613. this.tableData.taskCreate = createStatsRes.rows.map(item => ({
  614. ...item,
  615. taskType: item.taskType || '无类型'
  616. }))
  617. // 更新汇总统计
  618. this.summaryStats.taskCreate = [{
  619. label: '总任务数',
  620. value: createStatsRes.total || 0
  621. }, {
  622. label: '总积分',
  623. value: createStatsRes.rows.reduce((sum, item) => sum + (item.totalPoints || 0), 0)
  624. }]
  625. }
  626. uni.hideLoading()
  627. } else {
  628. // 加载服务单统计数据
  629. // 模拟数据
  630. setTimeout(() => {
  631. uni.hideLoading()
  632. // 这里可以更新数据
  633. }, 500)
  634. }
  635. } catch (e) {
  636. uni.hideLoading()
  637. console.error('加载数据失败', e)
  638. }
  639. },
  640. async loadMore(type) {
  641. // 根据类型加载更多数据
  642. this.loading[type] = true
  643. // 模拟加载更多
  644. setTimeout(() => {
  645. if (type === 'task') {
  646. // 添加更多任务数据
  647. this.tableData.task.push(...this.getMoreTaskData())
  648. } else {
  649. // 添加更多服务单数据
  650. this.tableData.service.push(...this.getMoreServiceData())
  651. }
  652. this.loading[type] = false
  653. }, 1000)
  654. },
  655. getMoreTaskData() {
  656. // 返回更多任务数据
  657. return [{
  658. taskType: '更多任务',
  659. points: '30',
  660. applicant: '测试*员',
  661. status: 'completed',
  662. statusText: '已完成',
  663. receiveTime: '2025-09-26'
  664. },
  665. {
  666. taskType: '更多任务2',
  667. points: '50',
  668. applicant: '测试*员2',
  669. status: 'pendingReview',
  670. statusText: '待审核',
  671. receiveTime: '2025-09-27'
  672. }
  673. ]
  674. },
  675. getMoreServiceData() {
  676. // 返回更多服务单数据
  677. return [{
  678. serviceNo: 'FW20251222004',
  679. serviceType: '维修服务',
  680. customerName: '赵*强',
  681. status: 'closed',
  682. statusText: '已关闭',
  683. createTime: '2025-12-22'
  684. },
  685. {
  686. serviceNo: 'FW20251221005',
  687. serviceType: '投诉建议',
  688. customerName: '孙*丽',
  689. status: 'cancelled',
  690. statusText: '已取消',
  691. createTime: '2025-12-21'
  692. }
  693. ]
  694. },
  695. // 根据taskType获取任务类型名称
  696. getTaskTypeName(taskType) {
  697. // 任务类型映射
  698. const taskTypeMap = {
  699. '1': '医生坐诊',
  700. '2': '科普讲座',
  701. '3': '学术讲座',
  702. '4': '科普文章',
  703. '5': '科普短视频',
  704. '6': '科普长视频',
  705. '7': '空中课堂',
  706. '8': '用药调研',
  707. '9': '问卷调研',
  708. '10': '社群咨询',
  709. '11': '健康问答'
  710. }
  711. return taskTypeMap[taskType] || '未知类型'
  712. }
  713. }
  714. }
  715. </script>
  716. <style lang="scss" scoped>
  717. .container {
  718. min-height: 100vh;
  719. background: #f7f8fa;
  720. padding-bottom: 20rpx;
  721. }
  722. /* 统计类型切换样式 */
  723. .stat-tabs {
  724. display: flex;
  725. background: #fff;
  726. padding: 24rpx;
  727. .stat-tab {
  728. flex: 1;
  729. text-align: center;
  730. padding: 20rpx 0;
  731. font-size: 32rpx;
  732. color: #999;
  733. position: relative;
  734. &.active {
  735. color: #333333;
  736. font-weight: bold;
  737. &::after {
  738. content: '';
  739. position: absolute;
  740. bottom: 0;
  741. left: 50%;
  742. transform: translateX(-50%);
  743. width: 60rpx;
  744. height: 6rpx;
  745. background: #388BFF;
  746. border-radius: 3rpx;
  747. }
  748. }
  749. }
  750. }
  751. .date-filter-bar {
  752. display: flex;
  753. align-items: center;
  754. justify-content: space-between;
  755. padding: 24rpx;
  756. background: #fff;
  757. .date-range {
  758. display: flex;
  759. align-items: center;
  760. gap: 8rpx;
  761. .date-text {
  762. font-family: PingFang SC, PingFang SC;
  763. font-weight: 500;
  764. font-size: 28rpx;
  765. color: #333333;
  766. }
  767. .arrow-down {
  768. font-size: 20rpx;
  769. color: #333333;
  770. }
  771. }
  772. .filter-btn {
  773. display: flex;
  774. align-items: center;
  775. gap: 8rpx;
  776. font-size: 28rpx;
  777. color: #999999;
  778. }
  779. }
  780. .w32 {
  781. width: 32rpx;
  782. }
  783. .h32 {
  784. height: 32rpx;
  785. }
  786. .mb16 {
  787. margin-bottom: 16rpx;
  788. }
  789. .status-tag {
  790. padding: 4rpx 12rpx;
  791. border-radius: 4rpx;
  792. font-size: 24rpx;
  793. &.uncompleted,
  794. &.pending {
  795. background: #FFF3E0;
  796. color: #FF9800;
  797. }
  798. &.completed {
  799. background: #E8F5E9;
  800. color: #4CAF50;
  801. }
  802. &.pendingReview {
  803. background: #E3F2FD;
  804. color: #2196F3;
  805. }
  806. &.rejected {
  807. background: #FFEBEE;
  808. color: #F44336;
  809. }
  810. &.processing {
  811. background: #E3F2FD;
  812. color: #2196F3;
  813. }
  814. &.closed,
  815. &.cancelled {
  816. background: #F5F5F5;
  817. color: #9E9E9E;
  818. }
  819. }
  820. /* 日期选择弹窗样式 */
  821. .date-picker-popup {
  822. position: fixed;
  823. top: 0;
  824. left: 0;
  825. right: 0;
  826. bottom: 0;
  827. background: rgba(0, 0, 0, 0.5);
  828. z-index: 999;
  829. display: flex;
  830. align-items: flex-end;
  831. }
  832. .date-picker-content {
  833. width: 100%;
  834. background: #fff;
  835. border-radius: 24rpx 24rpx 0 0;
  836. .picker-header {
  837. display: flex;
  838. align-items: center;
  839. justify-content: space-between;
  840. padding: 24rpx;
  841. .picker-cancel {
  842. font-size: 30rpx;
  843. color: #666;
  844. }
  845. .picker-title {
  846. font-size: 32rpx;
  847. font-weight: bold;
  848. color: #333;
  849. }
  850. .picker-confirm {
  851. font-size: 30rpx;
  852. color: #388BFF;
  853. }
  854. }
  855. .picker-body {
  856. padding: 24rpx;
  857. .date-item {
  858. display: flex;
  859. align-items: center;
  860. justify-content: space-between;
  861. padding: 24rpx 0;
  862. &:last-child {
  863. border-bottom: none;
  864. }
  865. .date-label {
  866. font-size: 30rpx;
  867. color: #333;
  868. }
  869. .date-value {
  870. font-size: 30rpx;
  871. color: #388BFF;
  872. }
  873. }
  874. }
  875. }
  876. /* 筛选弹窗样式 */
  877. .filter-popup {
  878. position: fixed;
  879. top: 0;
  880. left: 0;
  881. right: 0;
  882. bottom: 0;
  883. background: rgba(0, 0, 0, 0.5);
  884. z-index: 999;
  885. display: flex;
  886. align-items: flex-end;
  887. }
  888. .filter-content {
  889. width: 100%;
  890. background: #fff;
  891. border-radius: 24rpx 24rpx 0 0;
  892. padding: 24rpx;
  893. max-height: 80vh;
  894. overflow-y: auto;
  895. .filter-header {
  896. display: flex;
  897. align-items: center;
  898. justify-content: center;
  899. margin-bottom: 32rpx;
  900. position: relative;
  901. .filter-title {
  902. font-size: 32rpx;
  903. font-weight: bold;
  904. color: #333;
  905. }
  906. .filter-close-btn {
  907. width: 44rpx;
  908. height: 44rpx;
  909. position: absolute;
  910. right: 0;
  911. }
  912. }
  913. .filter-group {
  914. margin-bottom: 40rpx;
  915. .group-label {
  916. font-size: 28rpx;
  917. font-weight: bold;
  918. color: #333;
  919. margin-bottom: 20rpx;
  920. }
  921. .filter-tags {
  922. display: flex;
  923. flex-wrap: wrap;
  924. gap: 20rpx;
  925. .filter-tag {
  926. padding: 12rpx 24rpx;
  927. font-size: 28rpx;
  928. color: #333;
  929. background: #F7F8FA;
  930. border-radius: 70rpx;
  931. &.active {
  932. background: rgba(56, 139, 255, 0.15);
  933. color: #388BFF;
  934. }
  935. }
  936. }
  937. }
  938. .filter-actions {
  939. display: flex;
  940. gap: 24rpx;
  941. margin-top: 40rpx;
  942. padding-top: 24rpx;
  943. border-top: 1rpx solid #f0f0f0;
  944. .reset-btn,
  945. .confirm-btn {
  946. flex: 1;
  947. height: 88rpx;
  948. line-height: 88rpx;
  949. text-align: center;
  950. border-radius: 200rpx;
  951. font-size: 28rpx;
  952. }
  953. .reset-btn {
  954. background: #fff;
  955. color: #388BFF;
  956. border: 2rpx solid #388BFF;
  957. }
  958. .confirm-btn {
  959. background: #388BFF;
  960. color: #fff;
  961. }
  962. }
  963. }
  964. </style>