index.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. <template>
  2. <basic-container>
  3. <div class="search-content">
  4. <el-form :model="searchFrom" label-width="140px">
  5. <el-row>
  6. <el-col :span="6">
  7. <el-form-item label="执行包年度">
  8. <el-date-picker clearable class="w100" v-model="searchFrom.year" type="year" placeholder="请选择执行包年度" format="yyyy" value-format="yyyy"> </el-date-picker>
  9. </el-form-item>
  10. </el-col>
  11. <el-col :span="6">
  12. <el-form-item label="执行包季度">
  13. <el-select v-model="searchFrom.quarter" placeholder="请选择服务季度" class="w100" clearable>
  14. <el-option label="第一季度" :value="1"></el-option>
  15. <el-option label="第二季度" :value="2"></el-option>
  16. <el-option label="第三季度" :value="3"></el-option>
  17. <el-option label="第四季度" :value="4"></el-option>
  18. </el-select>
  19. </el-form-item>
  20. </el-col>
  21. <el-col :span="6">
  22. <el-form-item label="服务提交时间">
  23. <el-date-picker class="w100" clearable v-model="searchFrom.createTime" type="date" placeholder="选择日期" format="yyyy-MM-dd" value-format="yyyy-MM-dd">
  24. </el-date-picker>
  25. </el-form-item>
  26. </el-col>
  27. <el-col :span="6">
  28. <el-form-item label="服务类型">
  29. <el-select v-model="searchFrom.taskTypeId" placeholder="请选择服务类型" class="w100" clearable>
  30. <el-option :label="item.name" :value="item.id" v-for="item in taskTypeAvailArr" :key="item.id"></el-option>
  31. </el-select>
  32. </el-form-item>
  33. </el-col>
  34. </el-row>
  35. <el-row>
  36. <el-col :span="6">
  37. <el-form-item label="服务提供商">
  38. <el-select v-model="searchFrom.vendorId" placeholder="请选择服务提供商" class="w100" clearable filterable>
  39. <el-option v-for="item in deptListArr" :key="item.entId" :label="item.name" :value="item.entId" />
  40. </el-select>
  41. </el-form-item>
  42. </el-col>
  43. <el-col :span="6">
  44. <el-form-item label="代表姓名">
  45. <el-select v-model="searchFrom.salesId" placeholder="请选择代表姓名" class="w100" clearable filterable>
  46. <el-option v-for="item in userListArr" :key="item.userId" :label="item.realName" :value="item.userId" />
  47. </el-select>
  48. </el-form-item>
  49. </el-col>
  50. <el-col :span="6">
  51. <el-form-item label="产品所属生产企业">
  52. <el-select v-model="searchFrom.mahName" @change="mahNameChange" placeholder="请选择产品所属生产企业" class="w100" clearable>
  53. <el-option v-for="item in mahNameArr" :key="item.value" :label="item.label" :value="item.value" />
  54. </el-select>
  55. </el-form-item>
  56. </el-col>
  57. <el-col :span="6">
  58. <el-form-item label="关联产品名称">
  59. <el-select v-model="searchFrom.skuId" placeholder="请选择关联产品名称" class="w100" :disabled="drugDisabled" clearable filterable>
  60. <el-option v-for="item in currDrugList" :key="item.id" :label="item.drugNameTy" :value="item.id" />
  61. </el-select>
  62. </el-form-item>
  63. </el-col>
  64. </el-row>
  65. <el-row>
  66. <el-col :span="6">
  67. <el-form-item label="执行包所属省份">
  68. <el-select v-model="searchFrom.provAbbr" placeholder="请选择省份" class="w100" clearable>
  69. <el-option v-for="province in abbreviationsProvincesList" :key="province" :label="province" :value="province" />
  70. </el-select>
  71. </el-form-item>
  72. </el-col>
  73. <el-col :span="6">
  74. <el-form-item label="上游服务包名称">
  75. <el-select v-model="searchFrom.pkgIds" placeholder="请选择上游服务包名称" class="w100" clearable filterable>
  76. <el-option v-for="(item, index) in relPkgNameArr" :key="index" :label="item.label" :value="item.value" />
  77. </el-select>
  78. </el-form-item>
  79. </el-col>
  80. <el-col :span="6">
  81. <div class="search-btns">
  82. <el-button type="success" class="btn-style" @click="clickBtn">搜索</el-button>
  83. <el-button @click="clearSearch">清空</el-button>
  84. </div>
  85. </el-col>
  86. </el-row>
  87. </el-form>
  88. </div>
  89. <div class="check-btns">
  90. <el-button type="primary" @click="allClick">批量审核</el-button>
  91. <el-button type="primary" @click="quickAuditClick">快捷审核</el-button>
  92. </div>
  93. <div class="tips">当前表格已选择{{ totalNum }}项,总积分:{{ totalScore }}</div>
  94. <div class="table">
  95. <vxe-table
  96. ref="tableRef"
  97. border
  98. allAlign="center"
  99. show-overflow
  100. highlight-hover-row
  101. height="auto"
  102. empty-text="暂无匹配数据"
  103. :loading="tableLoading"
  104. :data="tableData"
  105. :scroll-y="{ enabled: false }"
  106. :scroll-x="{ enabled: true }"
  107. :header-cell-style="{ 'text-align': 'center' }"
  108. :cell-style="{ 'text-align': 'center' }"
  109. @checkbox-all="selectAllChangeEvent"
  110. @checkbox-change="selectChangeEvent"
  111. >
  112. <vxe-column type="checkbox" width="60"></vxe-column>
  113. <vxe-table-column type="seq" title="序号" width="100"></vxe-table-column>
  114. <vxe-table-column field="taskNumber" title="服务编号" width="200"></vxe-table-column>
  115. <vxe-table-column field="cycle" title="服务包周期" width="200"> </vxe-table-column>
  116. <vxe-table-column field="relPkgName" title="上游服务包名称" width="250"> </vxe-table-column>
  117. <vxe-table-column field="pkgName" title="服务包名称" width="200"></vxe-table-column>
  118. <vxe-table-column field="taskTypeName" title="服务类型" width="200"></vxe-table-column>
  119. <vxe-table-column field="taskScore" title="服务分值" width="100"></vxe-table-column>
  120. <vxe-table-column field="salesName" title="代表姓名" width="150"></vxe-table-column>
  121. <vxe-table-column field="createTime" title="服务提交时间" width="200"></vxe-table-column>
  122. <!-- <vxe-table-column field="checkResultName" title="服务当前状态" width="120"></vxe-table-column> -->
  123. <vxe-table-column field="entName" title="服务提供商" width="200"></vxe-table-column>
  124. <vxe-table-column field="mahName" title="产品所属生产企业" width="200"></vxe-table-column>
  125. <vxe-table-column field="skuName" title="关联产品名称" width="200"></vxe-table-column>
  126. <vxe-table-column field="provAbbr" title="执行包所属省份" width="130"></vxe-table-column>
  127. <vxe-table-column title="操作" fixed="right" width="130">
  128. <template slot-scope="scope">
  129. <div class="btns">
  130. <el-button type="text" @click="reviewBtn(scope.row)">审核</el-button>
  131. <el-button type="text" @click="serviceDetail(scope.row)">服务详情</el-button>
  132. </div>
  133. </template>
  134. </vxe-table-column>
  135. </vxe-table>
  136. </div>
  137. <vxe-pager
  138. :current-page.sync="page.currentPage"
  139. :page-size.sync="page.pageSize"
  140. :total="page.total"
  141. :page-sizes="page.pageSizes"
  142. @page-change="pageChange"
  143. :layouts="['PrevJump', 'PrevPage', 'JumpNumber', 'NextPage', 'NextJump', 'Sizes', 'FullJump', 'Total']"
  144. >
  145. </vxe-pager>
  146. <!-- 审核弹窗 -->
  147. <el-dialog title="审核" :close-on-click-modal="false" :visible.sync="reviewDialog" top="12vh" width="50%" :before-close="handleClose" center>
  148. <div class="reviewContent" v-loading="loading">
  149. <div class="check-tips">审核令牌有效期为3分钟,请在【{{ expireTime }}】前完成审核操作,如果失效,请重新打开弹窗。</div>
  150. <div class="review-detail" v-if="singleFlag">
  151. <div class="teskDetailsty">服务详情</div>
  152. <TaskDetail ref="taskDetailRef" :id="currentTaskId" />
  153. </div>
  154. <div class="teskDetailsty">审批意见</div>
  155. <el-form :model="taskForm" :rules="rules" ref="taskForm" label-width="100px">
  156. <el-form-item label="审批意见:" prop="checkResult" class="formitem-box">
  157. <el-radio-group v-model="taskForm.checkResult">
  158. <el-radio :label="true">通过</el-radio>
  159. <el-radio :label="false">拒绝</el-radio>
  160. </el-radio-group>
  161. </el-form-item>
  162. <el-form-item
  163. label="意见说明:"
  164. prop="checkMessage"
  165. class="formitem-box"
  166. :rules="[
  167. {
  168. required: !taskForm.checkResult,
  169. message: '请输入审批说明',
  170. trigger: 'blur'
  171. }
  172. ]"
  173. >
  174. <el-input type="textarea" v-model="taskForm.checkMessage"></el-input>
  175. </el-form-item>
  176. <refuseReason @selectRefuseReason="selectRefuseReason" />
  177. </el-form>
  178. </div>
  179. <span slot="footer" class="dialog-footer">
  180. <el-button @click="handleClose">取 消</el-button>
  181. <el-button type="primary" @click="checkForm" v-loading="loading">确 定</el-button>
  182. </span>
  183. </el-dialog>
  184. </basic-container>
  185. </template>
  186. <script>
  187. import {
  188. getTaskV2PageApi,
  189. getTaskTypeAvailApi,
  190. getDeptv2Avail,
  191. getDrug2Avail,
  192. getUserV2Avail,
  193. getTaskV2TokenApi,
  194. checkSingleApi,
  195. taskV2CheckBatchApi,
  196. getPkgV2Api
  197. } from '@/api/serviceManagement/servicesToBeReviewed/index.js';
  198. import { tableOptin } from './index.js';
  199. import { getDictType } from '@/api/common';
  200. import abbreviationsProvinces from '@/const/abbreviationsProvinces.js';
  201. import dayjs from 'dayjs';
  202. import { mapGetters } from 'vuex';
  203. import TaskDetail from './components/TaskDetail.vue';
  204. import refuseReason from '@/components/refuseReason';
  205. import Config from '@/settings';
  206. export default {
  207. components: { refuseReason, TaskDetail },
  208. data() {
  209. return {
  210. tableOptin,
  211. tableData: [],
  212. tableLoading: false,
  213. checkResultArr: [],
  214. taskTypeAvailArr: [],
  215. mahNameArr: [],
  216. deptListArr: [],
  217. drugListArr: [],
  218. userListArr: [],
  219. currDrugList: [],
  220. relPkgNameArr: {},
  221. searchLoading: false,
  222. drugDisabled: true,
  223. abbreviationsProvincesList: abbreviationsProvinces,
  224. nodeId: '',
  225. searchFrom: {
  226. year: '',
  227. taskTypeId: '',
  228. quarter: '',
  229. createTime: '',
  230. provAbbr: '',
  231. mahName: '',
  232. vendorId: '',
  233. salesId: '',
  234. skuId: '',
  235. pkgIds: '',
  236. taskStatus: 3
  237. },
  238. totalNum: 0,
  239. totalScore: 0,
  240. page: {
  241. pageSizes: [10, 50, 100, 300, 500],
  242. total: 0, // 总页数
  243. currentPage: 1, // 当前页数
  244. pageSize: 300 // 每页显示多少条
  245. },
  246. reviewDialog: false,
  247. taskForm: {
  248. checkResult: '',
  249. checkMessage: ''
  250. },
  251. rules: {
  252. checkResult: [{ required: true, message: '请选择审批意见', trigger: 'change' }]
  253. },
  254. singleFlag: false,
  255. checkIds: [],
  256. loading: false,
  257. currentTaskId: '',
  258. reviewToken: '',
  259. expireTime: ''
  260. };
  261. },
  262. computed: {
  263. ...mapGetters(['userInfo'])
  264. },
  265. created() {
  266. this.getDict();
  267. },
  268. mounted() {
  269. const roles = this.userInfo.roles;
  270. let nodeId = '';
  271. switch (true) {
  272. // 43 地市管理员
  273. case roles.includes(43):
  274. nodeId = 1;
  275. break;
  276. // 4 区域管理员
  277. case roles.includes(4):
  278. nodeId = 2;
  279. break;
  280. // 42 市场管理员: 获取审核节点为3和9 nodeid为12
  281. case roles.includes(42):
  282. nodeId = 12;
  283. break;
  284. // 40 商务管理员: 获取审核节点为3和8 nodeid为11
  285. case roles.includes(40):
  286. nodeId = 11;
  287. break;
  288. // 41 事业部分管领导
  289. case roles.includes(41):
  290. nodeId = 9;
  291. break;
  292. // 39 事业部总经理
  293. case roles.includes(39):
  294. nodeId = 15;
  295. break;
  296. default:
  297. nodeId = 1;
  298. }
  299. const currentYear = dayjs().format('YYYY');
  300. this.searchFrom.year = currentYear;
  301. this.nodeId = nodeId;
  302. this.searchFrom.nodeId = nodeId;
  303. this.getList();
  304. },
  305. methods: {
  306. getDict() {
  307. getDictType({ type: 'task_status' }).then((res) => {
  308. this.checkResultArr = res.data.data;
  309. });
  310. getDictType({ type: 'mah_name' }).then((res) => {
  311. this.mahNameArr = res.data.data;
  312. });
  313. getTaskTypeAvailApi().then((res) => {
  314. this.taskTypeAvailArr = res.data.data;
  315. });
  316. getDeptv2Avail().then((res) => {
  317. this.deptListArr = res.data.data;
  318. });
  319. getUserV2Avail().then((res) => {
  320. this.userListArr = res.data.data;
  321. });
  322. getDrug2Avail().then((res) => {
  323. this.drugListArr = res.data.data;
  324. });
  325. getPkgV2Api().then((res) => {
  326. let arr = [];
  327. let data = res.data.data;
  328. for (let key in data) {
  329. let obj = {
  330. label: key,
  331. value: data[key]
  332. };
  333. arr.push(obj);
  334. }
  335. this.relPkgNameArr = arr;
  336. });
  337. },
  338. async getList() {
  339. this.totalNum = 0;
  340. this.totalScore = 0;
  341. this.tableLoading = true;
  342. const obj = Object.assign(
  343. {
  344. current: this.page.currentPage,
  345. size: this.page.pageSize
  346. },
  347. this.searchFrom
  348. );
  349. const res = await getTaskV2PageApi(obj);
  350. this.tableLoading = false;
  351. if (res.data.code === 0) {
  352. this.tableData = res.data.data.records;
  353. this.tableData.forEach((item) => {
  354. const currA = this.taskTypeAvailArr.find((itn) => itn.id === item.taskTypeId);
  355. const currB = this.checkResultArr.find((itn) => itn.value === item.taskStatus);
  356. item.taskTypeName = currA?.name ?? '';
  357. item.checkResultName = currB?.label ?? '';
  358. item.cycle = item.pkgStartTime + '~' + item.pkgEndTime;
  359. });
  360. this.page.total = res.data.data.total;
  361. }
  362. },
  363. clickBtn() {
  364. this.tableData = [];
  365. this.page.currentPage = 1;
  366. this.getList();
  367. },
  368. clearSearch() {
  369. this.searchFrom = {
  370. year: '',
  371. taskTypeId: '',
  372. quarter: '',
  373. createTime: '',
  374. provAbbr: '',
  375. mahName: '',
  376. vendorId: '',
  377. salesId: '',
  378. skuId: '',
  379. pkgIds: '',
  380. taskStatus: 3
  381. };
  382. this.searchFrom.nodeId = this.nodeId;
  383. },
  384. pageChange(obj) {
  385. this.page.currentPage = obj.currentPage;
  386. this.page.pageSize = obj.pageSize;
  387. this.getList();
  388. },
  389. mahNameChange(e) {
  390. if (e) {
  391. const curr = this.drugListArr[e];
  392. this.currDrugList = curr;
  393. this.drugDisabled = false;
  394. } else {
  395. this.searchFrom.skuId = '';
  396. this.currDrugList = [];
  397. this.drugDisabled = true;
  398. }
  399. },
  400. selectAllChangeEvent(obj) {
  401. const records = obj.selection;
  402. this.checkIds = records.map((item) => item.taskId);
  403. this.totalNum = records.length;
  404. this.totalScore = records.reduce((pre, cur) => {
  405. return pre + (Number(cur.taskScore) || 0);
  406. }, 0);
  407. },
  408. selectChangeEvent(obj) {
  409. const records = obj.selection;
  410. this.checkIds = records.map((item) => item.taskId);
  411. this.totalNum = records.length;
  412. this.totalScore = records.reduce((pre, cur) => {
  413. return pre + (Number(cur.taskScore) || 0);
  414. }, 0);
  415. },
  416. selectRefuseReason(refuseReason) {
  417. let info;
  418. if (this.taskForm.checkMessage) {
  419. info = this.taskForm.checkMessage + refuseReason + ';';
  420. } else {
  421. info = refuseReason + ';';
  422. }
  423. this.$set(this.taskForm, 'checkMessage', info);
  424. },
  425. async reviewBtn(row) {
  426. this.currentTaskId = row.taskId;
  427. await this.getToken([row.taskId]);
  428. this.reviewDialog = true;
  429. this.singleFlag = true;
  430. this.$nextTick(() => {
  431. this.$refs.taskDetailRef.getInfo(row.taskId);
  432. });
  433. },
  434. handleClose() {
  435. this.reviewDialog = false;
  436. this.taskForm = {
  437. checkResult: '',
  438. checkMessage: ''
  439. };
  440. this.currentTaskId = '';
  441. },
  442. serviceDetail(row) {
  443. window.open(
  444. Config.outsideUrl + '/h5/#/pages/task/task-detail/index?id=' + row.taskId,
  445. 'newwindow',
  446. 'height=500, width=400, top=200, left=300, toolbar=no, menubar=no, scrollbars=no, resizable=no, location=no, status=no'
  447. );
  448. },
  449. checkForm() {
  450. this.$refs.taskForm.validate((valid) => {
  451. if (valid) {
  452. this.checkBtn();
  453. }
  454. });
  455. },
  456. async getToken(taskIds) {
  457. const tokenRes = await getTaskV2TokenApi({ taskIds: taskIds });
  458. if (tokenRes.data.code !== 0 || !tokenRes.data.data?.token) {
  459. return this.$message.error('获取token失败');
  460. }
  461. this.reviewToken = tokenRes.data.data.token;
  462. this.expireTime = tokenRes.data.data.expireTime;
  463. },
  464. async allClick() {
  465. if (!this.checkIds.length) {
  466. return this.$message.error('请选择待审核数据');
  467. }
  468. this.singleFlag = false;
  469. await this.getToken(this.checkIds);
  470. this.reviewDialog = true;
  471. },
  472. // 快捷审核
  473. quickAuditClick() {
  474. this.$router.push({
  475. path: '/serviceManagement/quickReview/index'
  476. });
  477. },
  478. async checkBtn() {
  479. let nodeId = '';
  480. const roles = this.userInfo.roles;
  481. switch (true) {
  482. // 43 地市管理员
  483. case roles.includes(43):
  484. nodeId = 2;
  485. break;
  486. // 4 区域管理员
  487. case roles.includes(4):
  488. nodeId = 3;
  489. break;
  490. // 42 市场管理员
  491. case roles.includes(42):
  492. nodeId = 8;
  493. break;
  494. // 40 商务管理员
  495. case roles.includes(40):
  496. nodeId = 9;
  497. break;
  498. // 39 事业部总经理
  499. case roles.includes(39):
  500. nodeId = 6;
  501. break;
  502. // 41 事业部分管领导
  503. case roles.includes(41):
  504. nodeId = 5;
  505. break;
  506. default:
  507. nodeId = 1;
  508. }
  509. let res;
  510. // 单条审核
  511. if (this.singleFlag) {
  512. let obj = {
  513. token: this.reviewToken,
  514. taskId: this.currentTaskId,
  515. checkResult: this.taskForm.checkResult,
  516. checkMessage: this.taskForm.checkMessage,
  517. nodeId: nodeId
  518. };
  519. res = await checkSingleApi(obj);
  520. } else {
  521. this.loading = true;
  522. let obj = {
  523. token: this.reviewToken,
  524. taskIds: this.checkIds,
  525. checkResult: this.taskForm.checkResult,
  526. checkMessage: this.taskForm.checkMessage,
  527. nodeId: nodeId
  528. };
  529. try {
  530. res = await taskV2CheckBatchApi(obj);
  531. this.loading = false;
  532. } catch (err) {
  533. this.loading = false;
  534. console.log('err', err);
  535. }
  536. }
  537. if (res.data.code === 0 && res.data.data) {
  538. this.$message.success('审核成功!');
  539. this.getList();
  540. this.handleClose();
  541. }
  542. }
  543. }
  544. };
  545. </script>
  546. <style lang="scss" scoped>
  547. .search-btns {
  548. margin-left: 50px;
  549. display: flex;
  550. .btn-style {
  551. margin-right: 15px;
  552. }
  553. }
  554. .tips {
  555. display: inline-block;
  556. padding: 0 5px;
  557. color: #85ce61;
  558. border-radius: 3px;
  559. line-height: 14px;
  560. font-size: 14px;
  561. }
  562. .table {
  563. min-height: 1000px;
  564. max-height: 1500px;
  565. }
  566. .teskDetailsty {
  567. width: 100%;
  568. height: 30px;
  569. line-height: 30px;
  570. font-size: 16px;
  571. font-weight: 600;
  572. color: #333333;
  573. border-bottom: 1px solid #d7d6d5;
  574. margin: 0 0 10px 0;
  575. }
  576. .check-btns {
  577. width: 150px;
  578. display: flex;
  579. justify-content: space-between;
  580. }
  581. .check-tips {
  582. margin-bottom: 5px;
  583. font-size: 16px;
  584. color: rgb(233 77 77);
  585. text-align: center;
  586. font-weight: 600;
  587. }
  588. </style>