statistics.vue 23 KB

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