xlTask.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. <template>
  2. <view class="container">
  3. <!-- 搜索+筛选栏 -->
  4. <view class="top-box">
  5. <view class="input-item">
  6. <image class="icon search-icon" src="@/static/image/search.png" mode="widthFix"></image>
  7. <input placeholder="请输入客户姓名/机构名称" placeholder-class="placeholder" />
  8. </view>
  9. <view class="filter-item" @click="showFilter = true">
  10. <image class="filter-icon" src="@/static/image/icon_select.png" mode="widthFix"></image>
  11. <text>筛选</text>
  12. </view>
  13. </view>
  14. <!-- 顶部选项卡:任务列表/审核列表 -->
  15. <view class="top-tabs">
  16. <view class="top-tab-item" :class="{ active: currentTopTab === 'task' }" @click="switchTopTab('task')">
  17. 任务列表
  18. </view>
  19. <view class="top-tab-item" :class="{ active: currentTopTab === 'audit' }" @click="switchTopTab('audit')">
  20. 审核列表
  21. </view>
  22. </view>
  23. <!-- 动态子标签 -->
  24. <view class="sub-tabs">
  25. <view class="sub-tab-item" :class="{ active: currentSubTab === item.value }"
  26. @click="currentSubTab = item.value" v-for="(item, index) in currentSubTabsList" :key="index">
  27. {{ item.label }}
  28. </view>
  29. </view>
  30. <!-- 列表内容 -->
  31. <scroll-view class="content" scroll-y>
  32. <view class="task-card" v-for="(item, index) in currentList" :key="index" @click="goDetails(item)">
  33. <!-- 任务/审核标题+状态 -->
  34. <view class="card-top">
  35. <text class="card-task-title">{{ item.taskTitle }}</text>
  36. <view class="status-tag" :class="item.statusClass">{{ item.statusText }}</view>
  37. </view>
  38. <view class="card-content">
  39. <!-- 讲者信息 -->
  40. <view class="speaker-info">
  41. <text class="speaker-name">{{ item.doctorName||'未命名' }}</text>
  42. <view class="level-tag" v-if="item.level">{{ item.level }}</view>
  43. </view>
  44. <!-- 医院+科室 -->
  45. <view class="org-info">
  46. <image class="org-icon" src="@/static/image/icon_hospital.png" mode="widthFix"></image>
  47. <text>{{ item.hospital ||'-' }}</text>
  48. <view class="line"></view>
  49. <text>{{ item.department||'-' }}</text>
  50. </view>
  51. <!-- 标签组 -->
  52. <view class="card-tags">
  53. <view class="tag-left-group">
  54. <view class="tag-item video-tag" v-if="item.videoType">
  55. <image class="video-tag-icon" src="@/static/image/icon_longvideo.png" mode="widthFix">
  56. </image>
  57. <text>{{ item.videoType ||'-'}}</text>
  58. </view>
  59. <view class="tag-item category-tag" v-if="item.category">
  60. {{ item.category }}
  61. </view>
  62. </view>
  63. <view class="tag-right-group">
  64. <view class="tag-item points-tag">{{ item.points||'0' }}积分</view>
  65. <view class="tag-item count-tag">{{ item.count ||'0' }}个</view>
  66. <view class="tag-item count-tag">院内</view>
  67. </view>
  68. </view>
  69. <!-- 时间信息 -->
  70. <view class="time-info">
  71. <view class="time-item">
  72. <text>申请时间:</text>
  73. <text>{{ item.applyTime ||'-'}}</text>
  74. </view>
  75. <view class="time-item" v-if="currentTopTab === 'task'">
  76. <text>完成时间:</text>
  77. <text>{{ item.updateTime || '-' }}</text>
  78. </view>
  79. <view class="time-item" v-if="currentTopTab === 'task'">
  80. <text>完成审核:</text>
  81. <text>{{ item.auditStatus || '-' }}</text>
  82. </view>
  83. </view>
  84. <!-- 操作按钮 -->
  85. <view class="operate-btn-group" v-if="currentTopTab === 'task'">
  86. <view class="share-btn" @click="handleShare(item)">
  87. <image class="share-icon" src="/static/image/icon_share.png" mode="widthFix"></image>
  88. <text>分享</text>
  89. </view>
  90. <view class="btn-group">
  91. <view class="btn" v-if="item.showDelete" @click.stop="handleTaskDelete(item, index)">删除</view>
  92. <view class="btn" @click.stop="handleCopy(item)">复制创建</view>
  93. </view>
  94. </view>
  95. <view class="operate-btn-group flex-end" v-else>
  96. <view class="btn-group">
  97. <view class="btn" v-if="item.showDelete" @click.stop="handleAuditDelete(item, index)">删除</view>
  98. <view class="btn" v-if="item.showEdit" @click.stop="handleEdit(item)">编辑</view>
  99. </view>
  100. </view>
  101. <!-- 状态印章 -->
  102. <image class="status-seal" :src="item.sealImg" mode="widthFix" v-if="currentTopTab === 'task'">
  103. </image>
  104. </view>
  105. </view>
  106. </scroll-view>
  107. <view class="side">
  108. <view class="side-item" >
  109. <image class="icon" @click="goCreate" src="/static/image/btn_add.png"></image>
  110. </view>
  111. <view class="side-item" >
  112. <image class="icon" @click="goData" src="/static/image/btn_data.png"></image>
  113. </view>
  114. </view>
  115. <!-- 筛选弹窗 -->
  116. <view class="filter-popup" v-if="showFilter" @click="closeFilter">
  117. <view class="filter-content" @click.stop>
  118. <view class="filter-header">
  119. <view class="filter-title">筛选</view>
  120. <image class="filter-close-btn" src="@/static/image/icon_close.png" @click="closeFilter"
  121. mode="widthFix"></image>
  122. </view>
  123. <view class="filter-form">
  124. <!-- 任务申请时间 -->
  125. <view class="filter-section">
  126. <view class="section-label">任务申请时间</view>
  127. <view class="time-range">
  128. <view class="time-input" @click="openDatePicker('applyTimeStart')">{{ filters.applyTimeStart || '开始时间' }}</view>
  129. <view class="time-separator">-</view>
  130. <view class="time-input" @click="openDatePicker('applyTimeEnd')">{{ filters.applyTimeEnd || '结束时间' }}</view>
  131. </view>
  132. </view>
  133. <!-- 任务完成时间 -->
  134. <view class="filter-section" v-if="currentTopTab === 'audit'">
  135. <view class="section-label">任务完成时间</view>
  136. <view class="time-range">
  137. <view class="time-input" @click="openDatePicker('finishTimeStart')">{{ filters.finishTimeStart || '开始时间' }}</view>
  138. <view class="time-separator">-</view>
  139. <view class="time-input" @click="openDatePicker('finishTimeEnd')">{{ filters.finishTimeEnd || '结束时间' }}</view>
  140. </view>
  141. </view>
  142. <!-- 完成审核时间 -->
  143. <view class="filter-section" v-if="currentTopTab === 'audit'">
  144. <view class="section-label">完成审核时间</view>
  145. <view class="time-range">
  146. <view class="time-input" @click="openDatePicker('auditTimeStart')">{{ filters.auditTimeStart || '开始时间' }}</view>
  147. <view class="time-separator">-</view>
  148. <view class="time-input" @click="openDatePicker('auditTimeEnd')">{{ filters.auditTimeEnd || '结束时间' }}</view>
  149. </view>
  150. </view>
  151. <!-- 任务归属 -->
  152. <view class="filter-section">
  153. <view class="section-label">任务归属</view>
  154. <view class="btn-group">
  155. <view class="filter-btn" :class="{ active: filters.taskBelong === 'my' }"
  156. @click="filters.taskBelong = 'my'">我的任务</view>
  157. <view class="filter-btn" :class="{ active: filters.taskBelong === 'dept' }"
  158. @click="filters.taskBelong = 'dept'">部门任务</view>
  159. </view>
  160. </view>
  161. <!-- 完结状态 -->
  162. <view class="filter-section" v-if="currentTopTab === 'audit'">
  163. <view class="section-label">完结状态</view>
  164. <view class="btn-group">
  165. <view class="filter-btn" :class="{ active: filters.finishStatus === 'unfinished' }"
  166. @click="filters.finishStatus = 'unfinished'">未完结</view>
  167. <view class="filter-btn" :class="{ active: filters.finishStatus === 'finished' }"
  168. @click="filters.finishStatus = 'finished'">已完结</view>
  169. </view>
  170. </view>
  171. <!-- 归属类型 -->
  172. <view class="filter-section">
  173. <view class="section-label">归属类型</view>
  174. <view class="btn-group">
  175. <view class="filter-btn" :class="{ active: filters.belongType === 'inner' }"
  176. @click="filters.belongType = 'inner'">院内</view>
  177. <view class="filter-btn" :class="{ active: filters.belongType === 'outer' }"
  178. @click="filters.belongType = 'outer'">院外</view>
  179. </view>
  180. </view>
  181. </view>
  182. <view class="filter-actions">
  183. <view class="reset-btn" @click="resetFilters">重置</view>
  184. <view class="confirm-btn" @click="confirmFilters">确定</view>
  185. </view>
  186. </view>
  187. </view>
  188. </view>
  189. </template>
  190. <script>
  191. import { list, deleted, company } from '@/api/task.js';
  192. export default {
  193. data() {
  194. return {
  195. showFilter: false,
  196. filters: {
  197. applyTimeStart: '',
  198. applyTimeEnd: '',
  199. finishTimeStart: '',
  200. finishTimeEnd: '',
  201. auditTimeStart: '',
  202. auditTimeEnd: '',
  203. taskBelong: 'my',
  204. finishStatus: '',
  205. belongType: ''
  206. },
  207. currentTopTab: 'task',
  208. currentSubTab: 'all',
  209. taskSubTabs: [{
  210. label: '全部任务',
  211. value: 'all'
  212. },
  213. {
  214. label: '未完成',
  215. value: 'unfinished'
  216. },
  217. {
  218. label: '已完成',
  219. value: 'finished'
  220. }
  221. ],
  222. auditSubTabs: [{
  223. label: '全部',
  224. value: 'all'
  225. },
  226. {
  227. label: '创建待审批',
  228. value: 'createPending'
  229. },
  230. {
  231. label: '完成待审批',
  232. value: 'finishPending'
  233. },
  234. {
  235. label: '驳回',
  236. value: 'rejected'
  237. }
  238. ],
  239. taskList: [{
  240. taskTitle: '王小明医生的学术讲座',
  241. statusText: '待完成',
  242. statusClass: 'status-wait',
  243. speaker: '王小明',
  244. level: '一级',
  245. hospital: '湖南省人民医院',
  246. department: '口腔科',
  247. videoType: '长视频',
  248. category: '学术',
  249. points: 10,
  250. count: 1,
  251. applyTime: '2025-9-20 13:55',
  252. finishTime: '-',
  253. auditStatus: '-',
  254. showDelete: true,
  255. sealImg: '/static/image/img_unfinish.png'
  256. }],
  257. auditList: [{
  258. taskTitle: '王小明医生的学术讲座',
  259. statusText: '创建驳回',
  260. statusClass: 'status-rejected',
  261. speaker: '王小明',
  262. level: '一级',
  263. hospital: '湖南省人民医院',
  264. department: '口腔科',
  265. videoType: '长视频',
  266. category: '学术',
  267. points: 10,
  268. count: 1,
  269. applyTime: '2025-9-20 13:55',
  270. showDelete: true,
  271. showEdit: true
  272. }],
  273. userInfo: {
  274. companyId: ''
  275. }
  276. }
  277. },
  278. computed: {
  279. currentSubTabsList() {
  280. return this.currentTopTab === 'task' ? this.taskSubTabs : this.auditSubTabs;
  281. },
  282. currentList() {
  283. return this.currentTopTab === 'task' ? this.taskList : this.auditList;
  284. }
  285. },
  286. mounted() {
  287. this.loadData();
  288. },
  289. methods: {
  290. loadData() {
  291. // 加载任务列表数据
  292. this.getTaskList();
  293. // 加载公司项目设置
  294. this.getCompanySettings();
  295. },
  296. getTaskList() {
  297. // 构建请求参数
  298. const params = {
  299. ...this.filters,
  300. type: this.currentTopTab,
  301. status: this.currentSubTab
  302. };
  303. uni.showLoading({
  304. title: '加载中...'
  305. });
  306. list(params).then(res => {
  307. uni.hideLoading();
  308. if (res.code === 200 && res.rows) {
  309. // 处理任务列表数据
  310. if (this.currentTopTab === 'task') {
  311. this.taskList = res.rows.map(item => ({
  312. id: item.id,
  313. taskTitle: item.taskName,
  314. statusText: this.getStatusText(item.taskStatus),
  315. statusClass: this.getStatusClass(item.taskStatus),
  316. speaker: item.doctorName,
  317. level: '', // 接口返回数据中没有level字段
  318. hospital: item.deptName, // 使用deptName作为hospital
  319. department: item.deptName, // 使用deptName作为department
  320. videoType: item.taskTypeName, // 使用taskTypeName作为videoType
  321. category: item.taskTypeName, // 使用taskTypeName作为category
  322. points: item.taskIntegral,
  323. count: item.taskCount,
  324. applyTime: item.createTime,
  325. finishTime: item.finishAuditTime || '-',
  326. auditStatus: this.getAuditStatusText(item.finishAuditStatus),
  327. showDelete: true,
  328. sealImg: item.taskStatus === 1 ? '/static/image/img_finish.png' : '/static/image/img_unfinish.png'
  329. }));
  330. } else {
  331. this.auditList = res.rows.map(item => ({
  332. id: item.id,
  333. taskTitle: item.taskName,
  334. statusText: this.getAuditStatusText(item.createAuditStatus),
  335. statusClass: this.getAuditStatusClass(item.createAuditStatus),
  336. speaker: item.doctorName,
  337. level: '', // 接口返回数据中没有level字段
  338. hospital: item.deptName, // 使用deptName作为hospital
  339. department: item.deptName, // 使用deptName作为department
  340. videoType: item.taskTypeName, // 使用taskTypeName作为videoType
  341. category: item.taskTypeName, // 使用taskTypeName作为category
  342. points: item.taskIntegral,
  343. count: item.taskCount,
  344. applyTime: item.createTime,
  345. showDelete: true,
  346. showEdit: item.createAuditStatus === 2 || item.finishAuditStatus === 2 // 2表示驳回
  347. }));
  348. }
  349. } else {
  350. uni.showToast({
  351. title: '获取数据失败',
  352. icon: 'none'
  353. });
  354. }
  355. }).catch(err => {
  356. uni.hideLoading();
  357. uni.showToast({
  358. title: '网络错误',
  359. icon: 'none'
  360. });
  361. });
  362. },
  363. getCompanySettings() {
  364. // 获取公司项目设置
  365. if (this.userInfo.companyId) {
  366. company(this.userInfo.companyId).then(res => {
  367. if (res.code === 200) {
  368. console.log('公司项目设置:', res.data);
  369. // 处理公司项目设置数据
  370. }
  371. }).catch(err => {
  372. console.error('获取公司项目设置失败:', err);
  373. });
  374. }
  375. },
  376. getStatusText(status) {
  377. // 根据状态获取状态文本
  378. const statusMap = {
  379. 0: '未完成',
  380. 1: '已完成'
  381. };
  382. return statusMap[status] || '未知状态';
  383. },
  384. getStatusClass(status) {
  385. // 根据状态获取状态样式类
  386. const statusMap = {
  387. 0: 'status-wait',
  388. 1: 'status-finish'
  389. };
  390. return statusMap[status] || 'status-wait';
  391. },
  392. getAuditStatusText(status) {
  393. // 根据审核状态获取状态文本
  394. const statusMap = {
  395. 0: '待审核',
  396. 1: '已通过',
  397. 2: '已驳回'
  398. };
  399. return statusMap[status] || '-';
  400. },
  401. getAuditStatusClass(status) {
  402. // 根据审核状态获取状态样式类
  403. const statusMap = {
  404. 0: 'status-wait',
  405. 1: 'status-finish',
  406. 2: 'status-rejected'
  407. };
  408. return statusMap[status] || 'status-wait';
  409. },
  410. goCreate(){
  411. console.log('创建任务');
  412. uni.navigateTo({
  413. url: '/pages_task/createTask'
  414. })
  415. },
  416. goData(){
  417. console.log("跳转到数据页");
  418. uni.navigateTo({
  419. url: '/pages_task/statistics'
  420. })
  421. },
  422. goDetails(item) {
  423. console.log("跳转到任务详情页",item);
  424. // 直接跳转到任务详情页,传递id作为taskId参数
  425. uni.navigateTo({
  426. url: `/pages_task/taskDetail?taskId=${item.id}`
  427. });
  428. },
  429. switchTopTab(type) {
  430. this.currentTopTab = type;
  431. this.currentSubTab = 'all';
  432. this.loadData();
  433. },
  434. closeFilter() {
  435. this.showFilter = false
  436. },
  437. resetFilters() {
  438. this.filters = {
  439. applyTimeStart: '',
  440. applyTimeEnd: '',
  441. finishTimeStart: '',
  442. finishTimeEnd: '',
  443. auditTimeStart: '',
  444. auditTimeEnd: '',
  445. taskBelong: 'my',
  446. finishStatus: '',
  447. belongType: ''
  448. }
  449. },
  450. confirmFilters() {
  451. this.showFilter = false
  452. // 根据筛选条件重新加载数据
  453. this.loadData();
  454. },
  455. // 打开日期选择器
  456. openDatePicker(field) {
  457. const currentValue = this.filters[field];
  458. const defaultDate = currentValue ? new Date(currentValue) : new Date();
  459. uni.datePicker({
  460. type: 'date',
  461. start: '2020-01-01',
  462. end: new Date().toISOString().split('T')[0],
  463. current: currentValue || new Date().toISOString().split('T')[0],
  464. success: (res) => {
  465. this.filters[field] = res.value;
  466. }
  467. });
  468. },
  469. handleShare(item) {
  470. uni.showToast({
  471. title: '分享功能待实现',
  472. icon: 'none'
  473. })
  474. },
  475. handleTaskDelete(item, index) {
  476. uni.showModal({
  477. title: '提示',
  478. content: '确定要删除这条任务吗?',
  479. cancelText: '取消',
  480. confirmText: '删除',
  481. confirmColor: '#FF0000',
  482. success: (res) => {
  483. if (res.confirm) {
  484. uni.showLoading({
  485. title: '删除中...'
  486. });
  487. deleted(item.id).then(res => {
  488. uni.hideLoading();
  489. if (res.code === 200) {
  490. this.taskList.splice(index, 1);
  491. uni.showToast({
  492. title: '任务已删除',
  493. icon: 'success',
  494. duration: 1500
  495. });
  496. } else {
  497. uni.showToast({
  498. title: '删除失败',
  499. icon: 'none'
  500. });
  501. }
  502. }).catch(err => {
  503. uni.hideLoading();
  504. uni.showToast({
  505. title: '网络错误',
  506. icon: 'none'
  507. });
  508. });
  509. }
  510. }
  511. });
  512. },
  513. handleCopy(item) {
  514. uni.navigateTo({
  515. url: `/pages_task/createTask?id=${item.id}&copy=true`
  516. })
  517. },
  518. handleAuditDelete(item, index) {
  519. uni.showModal({
  520. title: '提示',
  521. content: '确定要删除这条审核项吗?',
  522. cancelText: '取消',
  523. confirmText: '删除',
  524. confirmColor: '#FF0000',
  525. success: (res) => {
  526. if (res.confirm) {
  527. uni.showLoading({
  528. title: '删除中...'
  529. });
  530. deleted(item.id).then(res => {
  531. uni.hideLoading();
  532. if (res.code === 200) {
  533. this.auditList.splice(index, 1);
  534. uni.showToast({
  535. title: '审核项已删除',
  536. icon: 'success',
  537. duration: 1500
  538. });
  539. } else {
  540. uni.showToast({
  541. title: '删除失败',
  542. icon: 'none'
  543. });
  544. }
  545. }).catch(err => {
  546. uni.hideLoading();
  547. uni.showToast({
  548. title: '网络错误',
  549. icon: 'none'
  550. });
  551. });
  552. }
  553. }
  554. });
  555. },
  556. handleEdit(item) {
  557. uni.navigateTo({
  558. url: `/pages_task/createTask?id=${item.id}&edit=true`
  559. })
  560. }
  561. }
  562. }
  563. </script>
  564. <style lang="scss" scoped>
  565. .container {
  566. min-height: 100vh;
  567. background: #f5f5f5;
  568. .top-box {
  569. padding: 20rpx 32rpx;
  570. display: flex;
  571. align-items: center;
  572. background: #fff;
  573. .input-item {
  574. display: flex;
  575. align-items: center;
  576. flex: 1;
  577. height: 72rpx;
  578. background: #F7F8FA;
  579. border-radius: 38rpx;
  580. .search-icon {
  581. width: 26rpx;
  582. height: 26rpx;
  583. margin: 0 10rpx 0 28rpx;
  584. }
  585. input {
  586. flex: 1;
  587. font-size: 28rpx;
  588. color: #333;
  589. }
  590. }
  591. .filter-item {
  592. margin-left: 24rpx;
  593. display: flex;
  594. align-items: center;
  595. font-size: 28rpx;
  596. color: #999;
  597. .filter-icon {
  598. width: 28rpx;
  599. height: 28rpx;
  600. margin-right: 10rpx;
  601. }
  602. }
  603. }
  604. .top-tabs {
  605. display: flex;
  606. background: #fff;
  607. .top-tab-item {
  608. flex: 1;
  609. text-align: center;
  610. padding: 24rpx 0;
  611. font-size: 28rpx;
  612. color: #999;
  613. transition: all 0.2s;
  614. &.active {
  615. color: #333;
  616. font-weight: 500;
  617. position: relative;
  618. &::after {
  619. content: '';
  620. position: absolute;
  621. bottom: 0;
  622. left: 50%;
  623. transform: translateX(-50%);
  624. border-radius: 3rpx;
  625. width: 80rpx;
  626. height: 6rpx;
  627. background: #388BFF;
  628. }
  629. }
  630. }
  631. }
  632. .sub-tabs {
  633. display: flex;
  634. justify-content: space-between;
  635. background: #fff;
  636. padding: 16rpx 32rpx;
  637. gap: 24rpx;
  638. .sub-tab-item {
  639. padding: 12rpx 24rpx;
  640. border-radius: 30rpx;
  641. font-size: 28rpx;
  642. color: #666;
  643. transition: all 0.2s;
  644. &.active {
  645. background: #EBF3FF;
  646. color: #388BFF;
  647. }
  648. }
  649. }
  650. .content {
  651. padding: 24rpx;
  652. box-sizing: border-box;
  653. min-height: calc(100vh - 300rpx);
  654. .task-card {
  655. background: #fff;
  656. border-radius: 24rpx;
  657. margin-bottom: 20rpx;
  658. border: 2rpx solid #E9F2FF;
  659. position: relative;
  660. padding-bottom: 24rpx;
  661. .card-top {
  662. display: flex;
  663. justify-content: space-between;
  664. align-items: center;
  665. background: linear-gradient(90deg, #E8F1FF 0%, #FFFFFF 100%);
  666. border-radius: 24rpx 24rpx 0 0;
  667. padding: 26rpx 24rpx;
  668. margin-bottom: 16rpx;
  669. .card-task-title {
  670. font-size: 32rpx;
  671. font-weight: 600;
  672. color: #333;
  673. }
  674. .status-tag {
  675. padding: 8rpx 16rpx;
  676. border-radius: 20rpx;
  677. font-size: 24rpx;
  678. &.status-wait {
  679. background: #E3F2FD;
  680. color: #2196F3;
  681. }
  682. &.status-finish {
  683. background: #E6FAEF;
  684. color: #07C160;
  685. }
  686. &.status-rejected {
  687. background: #FFF4F5;
  688. color: #CF3546;
  689. }
  690. &.status-createPending {
  691. background: #FFF8E6;
  692. color: #FF9500;
  693. }
  694. }
  695. }
  696. .card-content {
  697. padding: 0 24rpx;
  698. .speaker-info {
  699. display: flex;
  700. align-items: center;
  701. margin-bottom: 16rpx;
  702. .speaker-name {
  703. font-weight: 600;
  704. font-size: 32rpx;
  705. color: #333;
  706. }
  707. .level-tag {
  708. margin-left: 16rpx;
  709. padding: 2rpx 12rpx;
  710. font-size: 22rpx;
  711. color: #C89743;
  712. background: #FFF6E5;
  713. border-radius: 8rpx;
  714. }
  715. }
  716. .org-info {
  717. display: flex;
  718. align-items: center;
  719. margin-bottom: 16rpx;
  720. font-size: 28rpx;
  721. color: #666;
  722. .org-icon {
  723. width: 32rpx;
  724. height: 32rpx;
  725. margin-right: 16rpx;
  726. }
  727. .line {
  728. width: 2rpx;
  729. height: 28rpx;
  730. background: #EAEBEE;
  731. margin: 0 24rpx;
  732. }
  733. }
  734. .card-tags {
  735. display: flex;
  736. align-items: center;
  737. flex-wrap: wrap;
  738. gap: 12rpx;
  739. margin-bottom: 16rpx;
  740. .tag-left-group {
  741. display: flex;
  742. gap: 0;
  743. .video-tag {
  744. background: linear-gradient(90deg, #FFE9C7 0%, #F3D091 100%);
  745. border-radius: 8rpx 0 0 8rpx;
  746. color: #5D410F;
  747. display: flex;
  748. align-items: center;
  749. padding: 8rpx;
  750. font-size: 24rpx;
  751. .video-tag-icon {
  752. width: 28rpx;
  753. height: 28rpx;
  754. margin-right: 10rpx;
  755. }
  756. }
  757. .category-tag {
  758. border-radius: 0 8rpx 8rpx 0;
  759. border: 2rpx solid #F3D191;
  760. background: #FFFAF4;
  761. color: #5D410F;
  762. padding: 8rpx;
  763. font-size: 24rpx;
  764. }
  765. }
  766. .tag-right-group {
  767. display: flex;
  768. gap: 12rpx;
  769. .points-tag {
  770. border: 1rpx solid #388BFF;
  771. color: #388BFF;
  772. background: transparent;
  773. border-radius: 8rpx;
  774. padding: 8rpx;
  775. font-size: 24rpx;
  776. }
  777. .count-tag {
  778. border: 2rpx solid #E8E8E8;
  779. border-radius: 8rpx;
  780. padding: 8rpx;
  781. font-size: 24rpx;
  782. color: #666;
  783. }
  784. }
  785. }
  786. .time-info {
  787. font-size: 28rpx;
  788. color: #666;
  789. display: flex;
  790. flex-direction: column;
  791. gap: 8rpx;
  792. margin-bottom: 24rpx;
  793. .time-item {
  794. display: flex;
  795. &:first-child {
  796. color: #333;
  797. }
  798. }
  799. }
  800. .operate-btn-group {
  801. display: flex;
  802. align-items: center;
  803. justify-content: space-between;
  804. &.flex-end {
  805. justify-content: flex-end;
  806. }
  807. .share-btn {
  808. display: flex;
  809. align-items: center;
  810. font-size: 28rpx;
  811. color: #666;
  812. .share-icon {
  813. width: 36rpx;
  814. height: 36rpx;
  815. margin-right: 8rpx;
  816. }
  817. }
  818. .btn-group {
  819. display: flex;
  820. align-items: center;
  821. .btn {
  822. padding: 12rpx 32rpx;
  823. font-size: 28rpx;
  824. color: #388BFF;
  825. border: 2rpx solid #388BFF;
  826. margin-left: 16rpx;
  827. border-radius: 34rpx;
  828. transition: all 0.2s;
  829. &:active {
  830. background: #EBF3FF;
  831. }
  832. }
  833. }
  834. }
  835. .status-seal {
  836. position: absolute;
  837. right: 24rpx;
  838. bottom: 156rpx;
  839. width: 152rpx;
  840. height: 142rpx;
  841. opacity: 0.8;
  842. }
  843. }
  844. }
  845. }
  846. .side {
  847. position: fixed;
  848. top: 60%;
  849. right: 36rpx;
  850. display: flex;
  851. flex-direction: column;
  852. align-items: center;
  853. z-index: 9;
  854. .side-item {
  855. width: 96rpx;
  856. height: 96rpx;
  857. margin-bottom: 28rpx;
  858. .icon {
  859. width: 100%;
  860. height: 100%;
  861. }
  862. &:active {
  863. opacity: 0.6;
  864. }
  865. }
  866. }
  867. .filter-popup {
  868. position: fixed;
  869. top: 0;
  870. left: 0;
  871. right: 0;
  872. bottom: 0;
  873. background: rgba(0, 0, 0, 0.5);
  874. z-index: 999;
  875. display: flex;
  876. align-items: flex-end;
  877. .filter-content {
  878. width: 100%;
  879. background: #fff;
  880. border-radius: 24rpx 24rpx 0 0;
  881. padding: 32rpx;
  882. .filter-header {
  883. display: flex;
  884. align-items: center;
  885. justify-content: center;
  886. margin-bottom: 32rpx;
  887. position: relative;
  888. .filter-title {
  889. font-size: 32rpx;
  890. font-weight: bold;
  891. color: #333;
  892. }
  893. .filter-close-btn {
  894. position: absolute;
  895. right: 0;
  896. width: 44rpx;
  897. height: 44rpx;
  898. }
  899. }
  900. .filter-form {
  901. padding: 0 0 24rpx 0;
  902. .filter-section {
  903. margin-bottom: 32rpx;
  904. .section-label {
  905. font-weight: 500;
  906. font-size: 28rpx;
  907. color: #333;
  908. margin-bottom: 24rpx;
  909. }
  910. .time-range {
  911. display: flex;
  912. align-items: center;
  913. gap: 16rpx;
  914. .time-input {
  915. flex: 1;
  916. height: 72rpx;
  917. text-align: center;
  918. background: #F7F8FA;
  919. border-radius: 8rpx;
  920. padding: 0 16rpx;
  921. font-size: 26rpx;
  922. color: #333;
  923. }
  924. .time-separator {
  925. font-size: 24rpx;
  926. color: #999;
  927. }
  928. }
  929. .btn-group {
  930. display: flex;
  931. gap: 16rpx;
  932. .filter-btn {
  933. width: 214rpx;
  934. height: 72rpx;
  935. background: #F7F8FA;
  936. border-radius: 70rpx 70rpx 70rpx 70rpx;
  937. line-height: 72rpx;
  938. text-align: center;
  939. font-size: 28rpx;
  940. transition: all 0.2s;
  941. color: #333333;
  942. &.active {
  943. background: rgba(56, 139, 255, 0.15);
  944. color: #388BFF;
  945. }
  946. }
  947. }
  948. }
  949. }
  950. .filter-actions {
  951. display: flex;
  952. gap: 24rpx;
  953. margin-top: 40rpx;
  954. .reset-btn,
  955. .confirm-btn {
  956. flex: 1;
  957. height: 80rpx;
  958. line-height: 80rpx;
  959. text-align: center;
  960. border-radius: 200rpx;
  961. font-size: 28rpx;
  962. transition: all 0.2s;
  963. }
  964. .reset-btn {
  965. background: #fff;
  966. color: #388BFF;
  967. border: 2rpx solid #388BFF;
  968. &:active {
  969. background: #EBF3FF;
  970. }
  971. }
  972. .confirm-btn {
  973. background: #388BFF;
  974. color: #fff;
  975. &:active {
  976. background: #2A78E5;
  977. }
  978. }
  979. }
  980. }
  981. }
  982. }
  983. </style>