xlTask.vue 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425
  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="selectSubTab(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. <!-- 任务列表 -->
  33. <view v-if="currentTopTab === 'task'">
  34. <view class="task-card" v-for="(item, index) in currentList" :key="index" @click="goDetails(item)">
  35. <!-- 任务标题+状态 -->
  36. <view class="card-top">
  37. <text class="card-task-title">{{ item.taskName }}</text>
  38. <view class="status-tag" :class="item.statusClass">{{ item.statusText }}</view>
  39. </view>
  40. <view class="card-content">
  41. <!-- 讲者信息 -->
  42. <view class="speaker-info">
  43. <text class="speaker-name">{{ item.doctorName||'未命名' }}</text>
  44. <view class="level-tag" v-if="item.level">{{ item.level }}</view>
  45. </view>
  46. <!-- 医院+科室 -->
  47. <view class="org-info">
  48. <image class="org-icon" src="@/static/image/icon_hospital.png" mode="widthFix"></image>
  49. <text>{{ item.deptName ||'-' }}</text>
  50. <view class="line"></view>
  51. <text>{{ item.deptName||'-' }}</text>
  52. </view>
  53. <!-- 标签组 -->
  54. <view class="card-tags">
  55. <view class="tag-left-group">
  56. <view class="tag-item video-tag" v-if="item.taskTypeName">
  57. <image class="video-tag-icon" src="@/static/image/icon_longvideo.png" mode="widthFix">
  58. </image>
  59. <text>{{ item.taskTypeName ||'-'}}</text>
  60. </view>
  61. <view class="tag-item category-tag" v-if="item.taskTypeName">
  62. {{ item.taskTypeName }}
  63. </view>
  64. </view>
  65. <view class="tag-right-group">
  66. <view class="tag-item points-tag">{{ item.taskIntegral||'0' }}积分</view>
  67. <view class="tag-item count-tag">{{ item.taskCount ||'0' }}个</view>
  68. <view class="tag-item count-tag">院内</view>
  69. </view>
  70. </view>
  71. <!-- 时间信息 -->
  72. <view class="time-info">
  73. <view class="time-item">
  74. <text>申请时间:</text>
  75. <text>{{ item.createTime ||'-'}}</text>
  76. </view>
  77. <view class="time-item">
  78. <text>完成时间:</text>
  79. <text>{{ item.updateTime || '-' }}</text>
  80. </view>
  81. <view class="time-item">
  82. <text>完成审核:</text>
  83. <text>{{ item.auditStatus || '-' }}</text>
  84. </view>
  85. </view>
  86. <!-- 操作按钮 -->
  87. <view class="operate-btn-group">
  88. <view class="share-btn" @click="handleShare(item)">
  89. <image class="share-icon" src="/static/image/icon_share.png" mode="widthFix"></image>
  90. <text>分享</text>
  91. </view>
  92. <view class="btn-group">
  93. <view class="btn" v-if="item.showDelete" @click.stop="handleTaskDelete(item, index)">删除</view>
  94. <!-- @click.stop="handleCopy(item)" -->
  95. <view class="btn" @click.stop="copyTask(item.id)" >复制创建</view>
  96. </view>
  97. </view>
  98. <!-- 状态印章 -->
  99. <image class="status-seal" :src="item.sealImg" mode="widthFix">
  100. </image>
  101. </view>
  102. </view>
  103. </view>
  104. <!-- 审核列表 -->
  105. <view v-else>
  106. <view class="task-card" v-for="(item, index) in currentList" :key="index" @click="goDetails(item)">
  107. <!-- 任务标题+状态 -->
  108. <view class="card-top">
  109. <text class="card-task-title">{{ item.auditName || '未设置' }}</text>
  110. <view class="status-tag" :class="item.statusClass">{{ item.statusText }}</view>
  111. </view>
  112. <view class="card-content">
  113. <!-- 讲者信息 -->
  114. <view class="speaker-info">
  115. <text class="speaker-name">{{ item.businessData.taskInfo.doctorVO.doctorName||'未命名' }}</text>
  116. <view class="level-tag" v-if="item.businessData.taskInfo.doctorVO.jobTitle">{{ item.businessData.taskInfo.doctorVO.jobTitle }}</view>
  117. </view>
  118. <!-- 医院+科室 -->
  119. <view class="org-info">
  120. <image class="org-icon" src="@/static/image/icon_hospital.png" mode="widthFix"></image>
  121. <text>{{ item.businessData.taskInfo.doctorVO.institution ||'-' }}</text>
  122. <view class="line"></view>
  123. <text>{{ item.businessData.taskInfo.doctorVO.department||'-' }}</text>
  124. </view>
  125. <!-- 标签组 -->
  126. <view class="card-tags">
  127. <view class="tag-left-group">
  128. <view class="tag-item video-tag" v-if="item.businessData.taskInfo.taskTypeName">
  129. <image class="video-tag-icon" src="@/static/image/icon_longvideo.png" mode="widthFix">
  130. </image>
  131. <text>{{ item.businessData.taskInfo.taskTypeName ||'-'}}</text>
  132. </view>
  133. <view class="tag-item category-tag" v-if="item.businessData.taskInfo.taskTypeName">
  134. {{ item.businessData.taskInfo.taskTypeName }}
  135. </view>
  136. </view>
  137. <view class="tag-right-group">
  138. <view class="tag-item points-tag">{{ item.businessData.taskInfo.doctorVO.taskIntegral||'0' }}积分</view>
  139. <view class="tag-item count-tag">{{ item.businessData.taskInfo.doctorVO.taskCount ||'0' }}个</view>
  140. <view class="tag-item count-tag">院内</view>
  141. </view>
  142. </view>
  143. <!-- 时间信息 -->
  144. <view class="time-info">
  145. <view class="time-item">
  146. <text>申请时间:</text>
  147. <text>{{ item.businessData.taskInfo.createTime ||'-'}}</text>
  148. </view>
  149. </view>
  150. <!-- 操作按钮 -->
  151. <view class="operate-btn-group">
  152. <view class="share-btn" @click="handleShare(item)">
  153. <image class="share-icon" src="@/static/image/icon_user.png" mode="widthFix"></image>
  154. <text>{{ item.initiatorName || '未命名' }}</text>
  155. </view>
  156. <view class="btn-group">
  157. <view class="btn" @click.stop="handleTaskDelete(item, index)">删除</view>
  158. <view class="btn" @click.stop="handleCopy(item)">编辑</view>
  159. </view>
  160. </view>
  161. <!-- 状态印章 -->
  162. <image class="status-seal" :src="item.sealImg" mode="widthFix">
  163. </image>
  164. </view>
  165. </view>
  166. </view>
  167. </scroll-view>
  168. <view class="side">
  169. <view class="side-item" >
  170. <image class="icon" @click="goCreate" src="/static/image/btn_add.png"></image>
  171. </view>
  172. <view class="side-item" >
  173. <image class="icon" @click="goData" src="/static/image/btn_data.png"></image>
  174. </view>
  175. </view>
  176. <!-- 筛选弹窗 -->
  177. <view class="filter-popup" v-if="showFilter" @click="closeFilter">
  178. <view class="filter-content" @click.stop>
  179. <view class="filter-header">
  180. <view class="filter-title">筛选</view>
  181. <image class="filter-close-btn" src="@/static/image/icon_close.png" @click="closeFilter"
  182. mode="widthFix"></image>
  183. </view>
  184. <view class="filter-form">
  185. <!-- 任务申请时间 -->
  186. <view class="filter-section">
  187. <view class="section-label">任务申请时间</view>
  188. <view class="time-range">
  189. <view class="time-input" @click="openDatePicker('applyTimeStart')">{{ filters.applyTimeStart || '开始时间' }}</view>
  190. <view class="time-separator">-</view>
  191. <view class="time-input" @click="openDatePicker('applyTimeEnd')">{{ filters.applyTimeEnd || '结束时间' }}</view>
  192. </view>
  193. </view>
  194. <!-- 任务完成时间 -->
  195. <view class="filter-section" v-if="currentTopTab === 'audit'">
  196. <view class="section-label">任务完成时间</view>
  197. <view class="time-range">
  198. <view class="time-input" @click="openDatePicker('finishTimeStart')">{{ filters.finishTimeStart || '开始时间' }}</view>
  199. <view class="time-separator">-</view>
  200. <view class="time-input" @click="openDatePicker('finishTimeEnd')">{{ filters.finishTimeEnd || '结束时间' }}</view>
  201. </view>
  202. </view>
  203. <!-- 完成审核时间 -->
  204. <view class="filter-section" v-if="currentTopTab === 'audit'">
  205. <view class="section-label">完成审核时间</view>
  206. <view class="time-range">
  207. <view class="time-input" @click="openDatePicker('auditTimeStart')">{{ filters.auditTimeStart || '开始时间' }}</view>
  208. <view class="time-separator">-</view>
  209. <view class="time-input" @click="openDatePicker('auditTimeEnd')">{{ filters.auditTimeEnd || '结束时间' }}</view>
  210. </view>
  211. </view>
  212. <!-- 任务归属 -->
  213. <view class="filter-section">
  214. <view class="section-label">任务归属</view>
  215. <view class="btn-group">
  216. <view class="filter-btn" :class="{ active: filters.taskBelong === '' }"
  217. @click="filters.taskBelong = ''">我的任务</view>
  218. <!-- <view class="filter-btn" :class="{ active: filters.taskBelong === 'dept' }"
  219. @click="filters.taskBelong = 'dept'">部门任务</view> -->
  220. </view>
  221. </view>
  222. <!-- 完结状态 -->
  223. <view class="filter-section" v-if="currentTopTab === 'audit'">
  224. <view class="section-label">完结状态</view>
  225. <view class="btn-group">
  226. <view class="filter-btn" :class="{ active: filters.finishStatus === 'unfinished' }"
  227. @click="filters.finishStatus = 'unfinished'">未完结</view>
  228. <view class="filter-btn" :class="{ active: filters.finishStatus === 'finished' }"
  229. @click="filters.finishStatus = 'finished'">已完结</view>
  230. </view>
  231. </view>
  232. <!-- 归属类型 -->
  233. <view class="filter-section">
  234. <view class="section-label">归属类型</view>
  235. <view class="btn-group">
  236. <view v-for="item in taskBelong" :key="item.dictValue"
  237. class="filter-btn" :class="{ active: filters.belongType === item.dictValue }"
  238. @click="filters.belongType = item.dictValue">{{ item.dictLabel }}</view>
  239. </view>
  240. </view>
  241. </view>
  242. <view class="filter-actions">
  243. <view class="reset-btn" @click="resetFilters">重置</view>
  244. <view class="confirm-btn" @click="confirmFilters">确定</view>
  245. </view>
  246. </view>
  247. </view>
  248. <!-- 日期选择器 -->
  249. <u-datetime-picker
  250. :show="showDatePicker"
  251. :value="datePickerValue"
  252. mode="date"
  253. @confirm="datePickerConfirm"
  254. @cancel="datePickerCancel"
  255. ></u-datetime-picker>
  256. </view>
  257. </template>
  258. <script>
  259. import { dicts } from '@/api/common.js';
  260. import utils from '@/utils/common.js'
  261. // auditList
  262. import { list, deleted, company ,auditList,copy} from '@/api/task.js';
  263. import { getPendingAuditList } from '@/api/audit.js';
  264. export default {
  265. data() {
  266. return {
  267. showFilter: false,
  268. showDatePicker: false,
  269. currentDateField: '',
  270. datePickerValue: new Date().getTime(),
  271. processTemplate: null,
  272. companySettingsLoaded: false,
  273. isLoading: false,
  274. filters: {
  275. applyTimeStart: '',
  276. applyTimeEnd: '',
  277. finishTimeStart: '',
  278. finishTimeEnd: '',
  279. auditTimeStart: '',
  280. auditTimeEnd: '',
  281. taskBelong: '',
  282. finishStatus: '',
  283. belongType: ''
  284. },
  285. currentTopTab: 'task',
  286. currentSubTab: '',
  287. taskSubTabs: [
  288. { label: '全部任务', value: '' }
  289. ],
  290. auditSubTabs: [{
  291. label: '全部',
  292. value: ''
  293. }
  294. ],
  295. taskList: [],
  296. auditList: [],
  297. userInfo: {
  298. companyId: ''
  299. },taskStatus:{
  300. },
  301. taskBelong: [],
  302. taskType: [],
  303. taskAuditStatus: [],
  304. auditStatus: []
  305. }
  306. },
  307. onLoad: async function(options) {
  308. // 先获取用户信息
  309. this.userInfo = JSON.parse(uni.getStorageSync('userInfo') || '{}');
  310. try {
  311. // 并行获取所有字典数据,提高性能
  312. const [taskBelongRes, taskTypeRes, processTemplateRes, taskAuditStatusRes] = await Promise.all([
  313. utils.getDicts("task_belong_type"),//任务归属
  314. utils.getDicts("task_type"),//任务类型
  315. utils.getDicts("FLOW_TEMPLATE"),//流程模板
  316. utils.getDicts("task_audit_status")//任务审核状态
  317. ]);
  318. this.taskBelong = taskBelongRes;
  319. this.taskType = taskTypeRes;
  320. this.processTemplate = processTemplateRes;
  321. this.taskAuditStatus = taskAuditStatusRes;
  322. console.log("task_audit_status", this.taskAuditStatus);
  323. // 构建审核子标签
  324. this.auditSubTabs = [{
  325. label: '全部',
  326. value: ''
  327. }];
  328. this.taskAuditStatus.forEach(item => {
  329. this.auditSubTabs.push({
  330. label: item.dictLabel,
  331. value: item.dictValue
  332. });
  333. });
  334. console.log('任务归属字典:', this.taskBelong);
  335. } catch (e) {
  336. console.log('获取字典数据失败:', e);
  337. }
  338. // 获取任务状态字典
  339. this.dicts();
  340. },
  341. computed: {
  342. currentSubTabsList() {
  343. return this.currentTopTab === 'task' ? this.taskSubTabs : this.auditSubTabs;
  344. },
  345. currentList() {
  346. return this.currentTopTab === 'task' ? this.taskList : this.auditList;
  347. }
  348. },
  349. mounted() {
  350. this.loadData();
  351. },
  352. methods: {
  353. //复制任务
  354. copyTask(id){
  355. console.log("复制",id);
  356. copy(id).then(res => {
  357. if (res.code === 200) {
  358. uni.showToast({
  359. title: '复制成功',
  360. icon: 'success'
  361. });
  362. this.loadData();
  363. } else {
  364. uni.showToast({
  365. title: res.msg || '复制失败',
  366. icon: 'none'
  367. });}})},
  368. //审批列表
  369. getAuditList() {
  370. // 构建请求参数
  371. const params = {
  372. userId: this.userInfo.userId,
  373. taskStatus: this.currentSubTab || '',
  374. status:'',
  375. companyId:this.userInfo.companyId,
  376. templateCode: this.templateCode||'',
  377. pageNum:1,
  378. pageSize:10,
  379. // 任务申请时间
  380. applyStartTime: this.filters.applyTimeStart || '',
  381. applyEndTime: this.filters.applyTimeEnd || '',
  382. // 任务完成时间
  383. taskFinishTimeStart: this.filters.finishTimeStart || '',
  384. taskFinishTimeEnd: this.filters.finishTimeEnd || '',
  385. // 任务完成审核时间
  386. finishAuditTimeStart: this.filters.auditTimeStart || '',
  387. finishAuditTimeEnd: this.filters.auditTimeEnd || '',
  388. // 完结状态
  389. finishStatus: this.filters.finishStatus === 'unfinished' ? 0 : this.filters.finishStatus === 'finished' ? 1 : ''
  390. };
  391. // 避免重复请求
  392. if (this.isLoading) return;
  393. this.isLoading = true;
  394. uni.showLoading({
  395. title: '加载中...'
  396. });
  397. getPendingAuditList(params).then(res => {
  398. this.isLoading = false;
  399. uni.hideLoading();
  400. if (res.code === 200 && res.rows) {
  401. // 处理审核列表数据
  402. this.auditList = res.rows.map(item => {
  403. // 从businessData.taskInfo中获取任务信息
  404. const taskInfo = item.businessData?.taskInfo || {};
  405. return {
  406. id: item.id,
  407. auditName: item.auditName,
  408. initiatorName: taskInfo.doctorName || item.initiatorName || '未知',
  409. createTime: item.createTime,
  410. statusText: this.getAuditStatusText(item.status),
  411. statusClass: this.getAuditStatusClass(item.status),
  412. doctorName: taskInfo.doctorName || item.initiatorName || '未知',
  413. initiatorId: item.initiatorId || '',
  414. sealImg: item.status === 1 ? '/static/image/img_finish.png' : '/static/image/img_unfinish.png',
  415. businessData: item.businessData || {},
  416. // 从taskInfo中获取其他需要的字段
  417. taskTitle: taskInfo.taskName || item.auditName,
  418. hospital: taskInfo.deptName || '-',
  419. department: taskInfo.deptName || '-',
  420. videoType: taskInfo.taskTypeName || '-',
  421. category: taskInfo.taskTypeName || '-',
  422. points: taskInfo.taskIntegral || 0,
  423. count: taskInfo.taskCount || 0,
  424. applyTime: item.createTime
  425. };
  426. });
  427. console.log("审核列表数据",this.auditList);
  428. } else {
  429. uni.showToast({
  430. title: '获取数据失败',
  431. icon: 'none'
  432. });
  433. }
  434. }).catch(err => {
  435. uni.hideLoading();
  436. uni.showToast({
  437. title: '网络错误',
  438. icon: 'none'
  439. });
  440. });
  441. },
  442. dicts() {
  443. dicts('task_status').then(res => {
  444. if (res.code === 200 ) {
  445. console.log(res.data)
  446. this.taskStatus = res.data;
  447. this.taskSubTabs = [
  448. { label: '全部任务', value: '' }
  449. ];
  450. res.data.forEach(item => {
  451. this.taskSubTabs.push({
  452. label: item.dictLabel,
  453. value: item.dictValue
  454. });
  455. });
  456. console.log('更新后的taskSubTabs:', this.taskSubTabs);
  457. }
  458. });
  459. },
  460. loadData() {
  461. // 根据当前标签加载对应数据
  462. if (this.currentTopTab === 'task') {
  463. this.getTaskList();
  464. } else if (this.currentTopTab === 'audit') {
  465. this.getAuditList();
  466. }
  467. // 只在首次加载时获取公司项目设置
  468. if (!this.companySettingsLoaded) {
  469. this.getCompanySettings();
  470. this.companySettingsLoaded = true;
  471. }
  472. },
  473. //任务列表
  474. getTaskList() {
  475. // 构建请求参数
  476. const params = {
  477. ...this.filters,
  478. type: this.currentTopTab,
  479. taskStatus: this.currentSubTab
  480. };
  481. // 避免重复请求
  482. if (this.isLoading) return;
  483. this.isLoading = true;
  484. uni.showLoading({
  485. title: '加载中...'
  486. });
  487. list(params).then(res => {
  488. this.isLoading = false;
  489. uni.hideLoading();
  490. if (res.code === 200 && res.rows) {
  491. // 处理任务列表数据
  492. this.taskList = res.rows.map(item => ({
  493. id: item.id,
  494. doctorName: item.doctorName,
  495. taskName: item.taskName,
  496. statusText: this.getStatusText(item.taskStatus),
  497. statusClass: this.getStatusClass(item.taskStatus),
  498. level: '', // 接口返回数据中没有level字段
  499. deptName: item.deptName, // 使用deptName作为deptName
  500. taskTypeName: item.taskTypeName,
  501. taskIntegral: item.taskIntegral,
  502. taskCount: item.taskCount,
  503. createTime: item.createTime,
  504. finishAuditTime: item.finishAuditTime || '-',
  505. auditStatus: this.getAuditStatusText(item.finishAuditStatus),
  506. showDelete: true,
  507. sealImg: item.taskStatus === 1 ? '/static/image/img_finish.png' : '/static/image/img_unfinish.png',
  508. finishAuditInstanceId: item.finishAuditInstanceId || '-',
  509. createAuditInstanceId: item.createAuditInstanceId || '-',
  510. }));
  511. } else {
  512. uni.showToast({
  513. title: '获取数据失败',
  514. icon: 'none'
  515. });
  516. }
  517. }).catch(err => {
  518. this.isLoading = false;
  519. uni.hideLoading();
  520. uni.showToast({
  521. title: '网络错误',
  522. icon: 'none'
  523. });
  524. });
  525. },
  526. getCompanySettings() {
  527. // 获取公司项目设置
  528. if (this.userInfo.companyId) {
  529. company(this.userInfo.companyId).then(res => {
  530. if (res.code === 200) {
  531. console.log('公司项目设置:', res.data);
  532. // 处理公司项目设置数据
  533. }
  534. }).catch(err => {
  535. console.error('获取公司项目设置失败:', err);
  536. });
  537. }
  538. },
  539. getStatusText(status) {
  540. // 根据状态获取状态文本
  541. const statusMap = {
  542. 0: '未完成',
  543. 1: '已完成'
  544. };
  545. return statusMap[status] || '未知状态';
  546. },
  547. getStatusClass(status) {
  548. // 根据状态获取状态样式类
  549. const statusMap = {
  550. 0: 'status-wait',
  551. 1: 'status-finish'
  552. };
  553. return statusMap[status] || 'status-wait';
  554. },
  555. getAuditStatusText(status) {
  556. // 根据审核状态获取状态文本
  557. const statusMap = {
  558. 0: '待审核',
  559. 1: '已通过',
  560. 2: '已驳回'
  561. };
  562. return statusMap[status] || '-';
  563. },
  564. getAuditStatusClass(status) {
  565. // 根据审核状态获取状态样式类
  566. const statusMap = {
  567. 0: 'status-wait',
  568. 1: 'status-finish',
  569. 2: 'status-rejected'
  570. };
  571. return statusMap[status] || 'status-wait';
  572. },
  573. getBusinessTypeLabel(businessType) {
  574. // 根据业务类型获取标签文本
  575. if (!businessType || !this.processTemplate) {
  576. return '未设置';
  577. }
  578. const dictItem = this.processTemplate.find(item => item.dictValue === businessType);
  579. return dictItem ? dictItem.dictLabel : businessType;
  580. },
  581. formatTime(time) {
  582. // 格式化时间
  583. if (!time) return '-';
  584. const date = new Date(time);
  585. const year = date.getFullYear();
  586. const month = String(date.getMonth() + 1).padStart(2, '0');
  587. const day = String(date.getDate()).padStart(2, '0');
  588. return `${year}-${month}-${day}`;
  589. },
  590. goCreate(){
  591. console.log('创建任务');
  592. uni.navigateTo({
  593. url: '/pages_task/createTask'
  594. })
  595. },
  596. goData(){
  597. console.log("跳转到数据页");
  598. uni.navigateTo({
  599. url: '/pages_task/statistics'
  600. })
  601. },
  602. goDetails(item) {
  603. console.log("跳转到任务详情页",item);
  604. // 直接跳转到任务详情页,传递id作为taskId参数
  605. let id = ''
  606. if(this.currentTopTab === 'task'){
  607. id = item.id
  608. }else if(this.currentTopTab === 'audit'){
  609. id = item.businessData.taskInfo.id
  610. }
  611. uni.navigateTo({
  612. url: `/pages_task/taskDetail?taskId=${item.createAuditInstanceId||item.businessData.taskInfo.createAuditInstanceId||''}&taskId2=${item.finishAuditInstanceId||item.businessData.taskInfo.finishAuditInstanceId||''}&id=${id||''}`
  613. });
  614. },
  615. selectSubTab(value) {
  616. this.currentSubTab = value;
  617. // 根据当前的 topTab 类型调用对应的方法
  618. if (this.currentTopTab === 'task') {
  619. this.getTaskList();
  620. } else if (this.currentTopTab === 'audit') {
  621. this.getAuditList();
  622. }
  623. },
  624. switchTopTab(type) {
  625. this.currentTopTab = type;
  626. this.currentSubTab = '';
  627. // 根据标签类型调用相应的加载方法
  628. if (type === 'task') {
  629. this.getTaskList();
  630. } else if (type === 'audit') {
  631. this.getAuditList();
  632. }
  633. },
  634. closeFilter() {
  635. this.showFilter = false
  636. },
  637. resetFilters() {
  638. this.filters = {
  639. applyTimeStart: '',
  640. applyTimeEnd: '',
  641. finishTimeStart: '',
  642. finishTimeEnd: '',
  643. auditTimeStart: '',
  644. auditTimeEnd: '',
  645. taskBelong: '',
  646. finishStatus: '',
  647. belongType: ''
  648. }
  649. },
  650. confirmFilters() {
  651. this.showFilter = false
  652. // 根据筛选条件重新加载数据
  653. this.loadData();
  654. },
  655. // 打开日期选择器
  656. openDatePicker(field) {
  657. console.log('openDatePicker called with field:', field);
  658. this.currentDateField = field;
  659. const currentValue = this.filters[field];
  660. if (currentValue) {
  661. this.datePickerValue = new Date(currentValue).getTime();
  662. } else {
  663. this.datePickerValue = new Date().getTime();
  664. }
  665. this.showDatePicker = true;
  666. },
  667. // 日期选择器确认
  668. datePickerConfirm(e) {
  669. console.log('datePickerConfirm:', e);
  670. const date = new Date(e.value);
  671. const year = date.getFullYear();
  672. const month = String(date.getMonth() + 1).padStart(2, '0');
  673. const day = String(date.getDate()).padStart(2, '0');
  674. const formattedDate = `${year}-${month}-${day}`;
  675. this.filters[this.currentDateField] = formattedDate;
  676. this.showDatePicker = false;
  677. },
  678. // 日期选择器取消
  679. datePickerCancel() {
  680. this.showDatePicker = false;
  681. },
  682. handleShare(item) {
  683. uni.showToast({
  684. title: '分享功能待实现',
  685. icon: 'none'
  686. })
  687. },
  688. handleTaskDelete(item, index) {
  689. uni.showModal({
  690. title: '提示',
  691. content: '确定要删除这条任务吗?',
  692. cancelText: '取消',
  693. confirmText: '删除',
  694. confirmColor: '#FF0000',
  695. success: (res) => {
  696. if (res.confirm) {
  697. uni.showLoading({
  698. title: '删除中...'
  699. });
  700. deleted(item.id).then(res => {
  701. uni.hideLoading();
  702. if (res.code === 200) {
  703. this.taskList.splice(index, 1);
  704. uni.showToast({
  705. title: '任务已删除',
  706. icon: 'success',
  707. duration: 1500
  708. });
  709. this.loadData();
  710. } else {
  711. uni.showToast({
  712. title: '删除失败',
  713. icon: 'none'
  714. });
  715. }
  716. }).catch(err => {
  717. uni.hideLoading();
  718. uni.showToast({
  719. title: '网络错误',
  720. icon: 'none'
  721. });
  722. });
  723. }
  724. }
  725. });
  726. },
  727. handleCopy(item) {
  728. console.log('去编辑', item);
  729. uni.navigateTo({
  730. url: `/pages_task/fillTask?id=${item.id}&doctorId=${item.initiatorId}&copy=true`
  731. })
  732. },
  733. handleAuditDelete(item, index) {
  734. uni.showModal({
  735. title: '提示',
  736. content: '确定要删除这条审核项吗?',
  737. cancelText: '取消',
  738. confirmText: '删除',
  739. confirmColor: '#FF0000',
  740. success: (res) => {
  741. if (res.confirm) {
  742. uni.showLoading({
  743. title: '删除中...'
  744. });
  745. deleted(item.id).then(res => {
  746. uni.hideLoading();
  747. if (res.code === 200) {
  748. this.auditList.splice(index, 1);
  749. uni.showToast({
  750. title: '审核项已删除',
  751. icon: 'success',
  752. duration: 1500
  753. });
  754. } else {
  755. uni.showToast({
  756. title: '删除失败',
  757. icon: 'none'
  758. });
  759. }
  760. }).catch(err => {
  761. uni.hideLoading();
  762. uni.showToast({
  763. title: '网络错误',
  764. icon: 'none'
  765. });
  766. });
  767. }
  768. }
  769. });
  770. },
  771. handleEdit(item) {
  772. uni.navigateTo({
  773. url: `/pages_task/createTask?id=${item.id}&edit=true`
  774. })
  775. }
  776. }
  777. }
  778. </script>
  779. <style lang="scss" scoped>
  780. .container {
  781. min-height: 100vh;
  782. background: #f5f5f5;
  783. .top-box {
  784. padding: 20rpx 32rpx;
  785. display: flex;
  786. align-items: center;
  787. background: #fff;
  788. .input-item {
  789. display: flex;
  790. align-items: center;
  791. flex: 1;
  792. height: 72rpx;
  793. background: #F7F8FA;
  794. border-radius: 38rpx;
  795. .search-icon {
  796. width: 26rpx;
  797. height: 26rpx;
  798. margin: 0 10rpx 0 28rpx;
  799. }
  800. input {
  801. flex: 1;
  802. font-size: 28rpx;
  803. color: #333;
  804. }
  805. }
  806. .filter-item {
  807. margin-left: 24rpx;
  808. display: flex;
  809. align-items: center;
  810. font-size: 28rpx;
  811. color: #999;
  812. .filter-icon {
  813. width: 28rpx;
  814. height: 28rpx;
  815. margin-right: 10rpx;
  816. }
  817. }
  818. }
  819. .top-tabs {
  820. display: flex;
  821. background: #fff;
  822. .top-tab-item {
  823. flex: 1;
  824. text-align: center;
  825. padding: 24rpx 0;
  826. font-size: 28rpx;
  827. color: #999;
  828. transition: all 0.2s;
  829. &.active {
  830. color: #333;
  831. font-weight: 500;
  832. position: relative;
  833. &::after {
  834. content: '';
  835. position: absolute;
  836. bottom: 0;
  837. left: 50%;
  838. transform: translateX(-50%);
  839. border-radius: 3rpx;
  840. width: 80rpx;
  841. height: 6rpx;
  842. background: #388BFF;
  843. }
  844. }
  845. }
  846. }
  847. .sub-tabs {
  848. display: flex;
  849. justify-content: space-between;
  850. background: #fff;
  851. padding: 16rpx 32rpx;
  852. gap: 24rpx;
  853. .sub-tab-item {
  854. padding: 12rpx 24rpx;
  855. border-radius: 30rpx;
  856. font-size: 28rpx;
  857. color: #666;
  858. transition: all 0.2s;
  859. &.active {
  860. background: #EBF3FF;
  861. color: #388BFF;
  862. }
  863. }
  864. }
  865. .content {
  866. padding: 24rpx;
  867. box-sizing: border-box;
  868. min-height: calc(100vh - 300rpx);
  869. .task-card {
  870. background: #fff;
  871. border-radius: 24rpx;
  872. margin-bottom: 20rpx;
  873. border: 2rpx solid #E9F2FF;
  874. position: relative;
  875. padding-bottom: 24rpx;
  876. .card-top {
  877. display: flex;
  878. justify-content: space-between;
  879. align-items: center;
  880. background: linear-gradient(90deg, #E8F1FF 0%, #FFFFFF 100%);
  881. border-radius: 24rpx 24rpx 0 0;
  882. padding: 26rpx 24rpx;
  883. margin-bottom: 16rpx;
  884. .card-task-title {
  885. font-size: 32rpx;
  886. font-weight: 600;
  887. color: #333;
  888. }
  889. .status-tag {
  890. padding: 8rpx 16rpx;
  891. border-radius: 20rpx;
  892. font-size: 24rpx;
  893. &.status-wait {
  894. background: #E3F2FD;
  895. color: #2196F3;
  896. }
  897. &.status-finish {
  898. background: #E6FAEF;
  899. color: #07C160;
  900. }
  901. &.status-rejected {
  902. background: #FFF4F5;
  903. color: #CF3546;
  904. }
  905. &.status-createPending {
  906. background: #FFF8E6;
  907. color: #FF9500;
  908. }
  909. }
  910. }
  911. .card-content {
  912. padding: 0 24rpx;
  913. .speaker-info {
  914. display: flex;
  915. align-items: center;
  916. margin-bottom: 16rpx;
  917. .speaker-name {
  918. font-weight: 600;
  919. font-size: 32rpx;
  920. color: #333;
  921. }
  922. .level-tag {
  923. margin-left: 16rpx;
  924. padding: 2rpx 12rpx;
  925. font-size: 22rpx;
  926. color: #C89743;
  927. background: #FFF6E5;
  928. border-radius: 8rpx;
  929. }
  930. }
  931. .org-info {
  932. display: flex;
  933. align-items: center;
  934. margin-bottom: 16rpx;
  935. font-size: 28rpx;
  936. color: #666;
  937. .org-icon {
  938. width: 32rpx;
  939. height: 32rpx;
  940. margin-right: 16rpx;
  941. }
  942. .line {
  943. width: 2rpx;
  944. height: 28rpx;
  945. background: #EAEBEE;
  946. margin: 0 24rpx;
  947. }
  948. }
  949. .card-tags {
  950. display: flex;
  951. align-items: center;
  952. flex-wrap: wrap;
  953. gap: 12rpx;
  954. margin-bottom: 16rpx;
  955. .tag-left-group {
  956. display: flex;
  957. gap: 0;
  958. .video-tag {
  959. background: linear-gradient(90deg, #FFE9C7 0%, #F3D091 100%);
  960. border-radius: 8rpx 0 0 8rpx;
  961. color: #5D410F;
  962. display: flex;
  963. align-items: center;
  964. padding: 8rpx;
  965. font-size: 24rpx;
  966. .video-tag-icon {
  967. width: 28rpx;
  968. height: 28rpx;
  969. margin-right: 10rpx;
  970. }
  971. }
  972. .category-tag {
  973. border-radius: 0 8rpx 8rpx 0;
  974. border: 2rpx solid #F3D191;
  975. background: #FFFAF4;
  976. color: #5D410F;
  977. padding: 8rpx;
  978. font-size: 24rpx;
  979. }
  980. }
  981. .tag-right-group {
  982. display: flex;
  983. gap: 12rpx;
  984. .points-tag {
  985. border: 1rpx solid #388BFF;
  986. color: #388BFF;
  987. background: transparent;
  988. border-radius: 8rpx;
  989. padding: 8rpx;
  990. font-size: 24rpx;
  991. }
  992. .count-tag {
  993. border: 2rpx solid #E8E8E8;
  994. border-radius: 8rpx;
  995. padding: 8rpx;
  996. font-size: 24rpx;
  997. color: #666;
  998. }
  999. }
  1000. }
  1001. .time-info {
  1002. font-size: 28rpx;
  1003. color: #666;
  1004. display: flex;
  1005. flex-direction: column;
  1006. gap: 8rpx;
  1007. margin-bottom: 24rpx;
  1008. .time-item {
  1009. display: flex;
  1010. &:first-child {
  1011. color: #333;
  1012. }
  1013. }
  1014. }
  1015. .operate-btn-group {
  1016. display: flex;
  1017. align-items: center;
  1018. justify-content: space-between;
  1019. &.flex-end {
  1020. justify-content: flex-end;
  1021. }
  1022. .share-btn {
  1023. display: flex;
  1024. align-items: center;
  1025. font-size: 28rpx;
  1026. color: #666;
  1027. .share-icon {
  1028. width: 36rpx;
  1029. height: 36rpx;
  1030. margin-right: 8rpx;
  1031. }
  1032. }
  1033. .btn-group {
  1034. display: flex;
  1035. align-items: center;
  1036. .btn {
  1037. padding: 12rpx 32rpx;
  1038. font-size: 28rpx;
  1039. color: #388BFF;
  1040. border: 2rpx solid #388BFF;
  1041. margin-left: 16rpx;
  1042. border-radius: 34rpx;
  1043. transition: all 0.2s;
  1044. &:active {
  1045. background: #EBF3FF;
  1046. }
  1047. }
  1048. }
  1049. }
  1050. .status-seal {
  1051. position: absolute;
  1052. right: 24rpx;
  1053. bottom: 156rpx;
  1054. width: 152rpx;
  1055. height: 142rpx;
  1056. opacity: 0.8;
  1057. }
  1058. }
  1059. }
  1060. .audit-card {
  1061. background: #f9f9f9;
  1062. border-radius: 24rpx;
  1063. margin-bottom: 20rpx;
  1064. border: 2rpx solid #E9F2FF;
  1065. position: relative;
  1066. padding-bottom: 24rpx;
  1067. .card-top {
  1068. display: flex;
  1069. justify-content: space-between;
  1070. align-items: center;
  1071. background: linear-gradient(90deg, #E8F1FF 0%, #FFFFFF 100%);
  1072. border-radius: 24rpx 24rpx 0 0;
  1073. padding: 26rpx 24rpx;
  1074. margin-bottom: 16rpx;
  1075. .card-task-title {
  1076. font-size: 32rpx;
  1077. font-weight: 600;
  1078. color: #333;
  1079. }
  1080. .status-tag {
  1081. padding: 8rpx 16rpx;
  1082. border-radius: 20rpx;
  1083. font-size: 24rpx;
  1084. &.status-wait {
  1085. background: #E3F2FD;
  1086. color: #2196F3;
  1087. }
  1088. &.status-finish {
  1089. background: #E6FAEF;
  1090. color: #07C160;
  1091. }
  1092. &.status-rejected {
  1093. background: #FFF4F5;
  1094. color: #CF3546;
  1095. }
  1096. &.status-createPending {
  1097. background: #FFF8E6;
  1098. color: #FF9500;
  1099. }
  1100. }
  1101. }
  1102. .card-content {
  1103. padding: 0 24rpx;
  1104. .time-info {
  1105. margin-bottom: 16rpx;
  1106. .time-item {
  1107. margin-bottom: 8rpx;
  1108. display: flex;
  1109. align-items: flex-start;
  1110. text {
  1111. font-size: 24rpx;
  1112. color: #999;
  1113. &.title {
  1114. color: #666;
  1115. margin-right: 8rpx;
  1116. white-space: nowrap;
  1117. }
  1118. &:not(.title) {
  1119. flex: 1;
  1120. word-break: break-word;
  1121. overflow-wrap: break-word;
  1122. }
  1123. }
  1124. }
  1125. }
  1126. .operate-btn-group {
  1127. display: flex;
  1128. justify-content: space-between;
  1129. align-items: center;
  1130. margin-top: 16rpx;
  1131. .share-btn {
  1132. display: flex;
  1133. align-items: center;
  1134. font-size: 24rpx;
  1135. color: #999;
  1136. .share-icon {
  1137. width: 36rpx;
  1138. height: 36rpx;
  1139. margin-right: 8rpx;
  1140. }
  1141. }
  1142. .date {
  1143. font-size: 24rpx;
  1144. color: #999;
  1145. }
  1146. }
  1147. }
  1148. }
  1149. }
  1150. .side {
  1151. position: fixed;
  1152. top: 60%;
  1153. right: 36rpx;
  1154. display: flex;
  1155. flex-direction: column;
  1156. align-items: center;
  1157. z-index: 9;
  1158. .side-item {
  1159. width: 96rpx;
  1160. height: 96rpx;
  1161. margin-bottom: 28rpx;
  1162. .icon {
  1163. width: 100%;
  1164. height: 100%;
  1165. }
  1166. &:active {
  1167. opacity: 0.6;
  1168. }
  1169. }
  1170. }
  1171. .filter-popup {
  1172. position: fixed;
  1173. top: 0;
  1174. left: 0;
  1175. right: 0;
  1176. bottom: 0;
  1177. background: rgba(0, 0, 0, 0.5);
  1178. z-index: 999;
  1179. display: flex;
  1180. align-items: flex-end;
  1181. .filter-content {
  1182. width: 100%;
  1183. background: #fff;
  1184. border-radius: 24rpx 24rpx 0 0;
  1185. padding: 32rpx;
  1186. .filter-header {
  1187. display: flex;
  1188. align-items: center;
  1189. justify-content: center;
  1190. margin-bottom: 32rpx;
  1191. position: relative;
  1192. .filter-title {
  1193. font-size: 32rpx;
  1194. font-weight: bold;
  1195. color: #333;
  1196. }
  1197. .filter-close-btn {
  1198. position: absolute;
  1199. right: 0;
  1200. width: 44rpx;
  1201. height: 44rpx;
  1202. }
  1203. }
  1204. .filter-form {
  1205. padding: 0 0 24rpx 0;
  1206. .filter-section {
  1207. margin-bottom: 32rpx;
  1208. .section-label {
  1209. font-weight: 500;
  1210. font-size: 28rpx;
  1211. color: #333;
  1212. margin-bottom: 24rpx;
  1213. }
  1214. .time-range {
  1215. display: flex;
  1216. align-items: center;
  1217. gap: 16rpx;
  1218. .time-input {
  1219. flex: 1;
  1220. line-height: 72rpx;
  1221. height: 72rpx;
  1222. text-align: center;
  1223. background: #F7F8FA;
  1224. border-radius: 8rpx;
  1225. padding: 0 16rpx;
  1226. font-size: 26rpx;
  1227. color: #333;
  1228. }
  1229. .time-separator {
  1230. font-size: 24rpx;
  1231. color: #999;
  1232. }
  1233. }
  1234. .btn-group {
  1235. display: flex;
  1236. gap: 16rpx;
  1237. .filter-btn {
  1238. width: 214rpx;
  1239. height: 72rpx;
  1240. background: #F7F8FA;
  1241. border-radius: 70rpx 70rpx 70rpx 70rpx;
  1242. line-height: 72rpx;
  1243. text-align: center;
  1244. font-size: 28rpx;
  1245. transition: all 0.2s;
  1246. color: #333333;
  1247. &.active {
  1248. background: rgba(56, 139, 255, 0.15);
  1249. color: #388BFF;
  1250. }
  1251. }
  1252. }
  1253. }
  1254. }
  1255. .filter-actions {
  1256. display: flex;
  1257. gap: 24rpx;
  1258. margin-top: 40rpx;
  1259. .reset-btn,
  1260. .confirm-btn {
  1261. flex: 1;
  1262. height: 80rpx;
  1263. line-height: 80rpx;
  1264. text-align: center;
  1265. border-radius: 200rpx;
  1266. font-size: 28rpx;
  1267. transition: all 0.2s;
  1268. }
  1269. .reset-btn {
  1270. background: #fff;
  1271. color: #388BFF;
  1272. border: 2rpx solid #388BFF;
  1273. &:active {
  1274. background: #EBF3FF;
  1275. }
  1276. }
  1277. .confirm-btn {
  1278. background: #388BFF;
  1279. color: #fff;
  1280. &:active {
  1281. background: #2A78E5;
  1282. }
  1283. }
  1284. }
  1285. }
  1286. }
  1287. }
  1288. </style>