detail.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. <template>
  2. <view class="content">
  3. <view class="title"> 项目详情 </view>
  4. <view class="main">
  5. <view class="list">
  6. <view class="detail">
  7. <span class="subTitle">项目名称:</span>
  8. <span class="detailContent">{{ currentProject.project_name }}</span>
  9. </view>
  10. <view class="detail" v-if="currentProject.TypeInfo">
  11. <span class="subTitle">项目类别:</span>
  12. <span class="detailContent">{{ currentProject.TypeInfo.name }}</span>
  13. </view>
  14. <view class="detail" v-if="currentProject.IndustryInfo">
  15. <span class="subTitle">行业名称:</span>
  16. <span class="detailContent">
  17. {{ currentProject.IndustryInfo.name }}
  18. </span>
  19. </view>
  20. <view class="detail">
  21. <span class="subTitle">流程:</span>
  22. <span class="detailContent">{{ currentProject.FlowInfo.name }}</span>
  23. </view>
  24. <view class="detail">
  25. <span class="subTitle">状态:</span>
  26. <span class="detailContent">
  27. {{ status[currentProject.project_status] }}
  28. </span>
  29. </view>
  30. <view class="detail">
  31. <span class="subTitle">节点:</span>
  32. <span class="detailContent">
  33. {{ `${currentProject.NodeInfo.node}(` }}
  34. <span v-if="currentProject.audit_status == 0">待提交</span>
  35. <span
  36. v-if="currentProject.audit_status == 1"
  37. :style="{ color: '#1890ff' }"
  38. >
  39. 审核中
  40. </span>
  41. <span
  42. v-if="currentProject.audit_status == 2"
  43. :style="{ color: '#f5222d' }"
  44. >
  45. 审核拒绝
  46. </span>
  47. <span
  48. v-if="currentProject.audit_status == 3"
  49. :style="{ color: '#a0d911' }"
  50. >
  51. 审核通过
  52. </span>
  53. <span>)</span>
  54. </span>
  55. </view>
  56. <view class="detail" v-if="currentProject.audit_status == 2">
  57. <span class="subTitle">拒绝原因:</span>
  58. <span class="detailContent">
  59. {{ currentProject.audit_comment }}
  60. </span>
  61. </view>
  62. <view class="detail" v-if="currentProject.location">
  63. <span class="subTitle">项目地区:</span>
  64. <span class="detailContent">
  65. {{ `${currentProject.location}(${currentProject.location_code})` }}
  66. </span>
  67. </view>
  68. <view class="detail" v-if="currentProject.name">
  69. <span class="subTitle">项目简称:</span>
  70. <span class="detailContent">{{ currentProject.name }}</span>
  71. </view>
  72. <view class="detail" v-if="currentProject.version">
  73. <span class="subTitle">项目批次:</span>
  74. <span class="detailContent">{{ currentProject.version }}期</span>
  75. </view>
  76. <view class="detail" v-if="currentProject.AuthorUser">
  77. <span class="subTitle">创建人:</span>
  78. <span class="detailContent">{{
  79. currentProject.AuthorUser.CName
  80. }}</span>
  81. </view>
  82. <view class="detail" v-if="currentProject.AuthorDepInfo">
  83. <span class="subTitle">所属部门:</span>
  84. <span class="detailContent">
  85. {{ currentProject.AuthorDepInfo.Name }}
  86. </span>
  87. </view>
  88. <view class="detail">
  89. <span class="subTitle">项目编号:</span>
  90. <span class="detailContent">
  91. {{ currentProject.project_full_code }}
  92. </span>
  93. </view>
  94. </view>
  95. <view class="title">审核详情 </view>
  96. <view class="list">
  97. <uni-steps
  98. :options="nodeList.list"
  99. :active="nodeList.currentIndex"
  100. direction="column"
  101. />
  102. </view>
  103. <uni-card title="操作" v-if="!this.auth">
  104. <view class="editBtns" slot="actions">
  105. <view class="edit" v-if="canEdit(0)" @click="onEdit()">编辑</view>
  106. <view class="edit" v-if="canEdit(0)" @click="onDelete()">删除</view>
  107. <view class="edit" v-if="canEdit(0)" @click="onSubmitAuth()">
  108. 提交审核
  109. </view>
  110. <view class="edit" v-if="canEdit(1)" @click="onMember()"
  111. >成员管理
  112. </view>
  113. <view class="edit" v-if="canEdit(2)" @click="onExecute()">
  114. 转执行
  115. </view>
  116. <view class="edit" v-if="canEdit(3)" @click="onWarranty()">
  117. 转质保
  118. </view>
  119. <view class="edit" v-if="canEdit(3)" @click="onOperate()">
  120. 转运营
  121. </view>
  122. </view>
  123. </uni-card>
  124. </view>
  125. <view class="group" v-if="this.auth && canAuth()">
  126. <button @click="onHandleAudit(0)" class="commit">审核拒绝</button>
  127. <button @click="onHandleAudit(1)" type="primary" class="commit">
  128. 审核通过
  129. </button>
  130. </view>
  131. <uni-popup ref="exePopup" type="dialog">
  132. <uni-popup-dialog
  133. title="转执行"
  134. type="info"
  135. @confirm="submitExecute"
  136. @close="onCancel"
  137. before-close
  138. >
  139. <uni-forms
  140. ref="exe"
  141. :modelValue="formData"
  142. label-position="left"
  143. :rules="exeRules"
  144. >
  145. <uni-forms-item required label="执行经理:" name="manager">
  146. <uni-data-picker
  147. class="depSelect"
  148. placeholder="请选择执行经理"
  149. :localdata="depUserTree"
  150. @change="changeManager"
  151. />
  152. </uni-forms-item>
  153. <uni-forms-item required label="合同状态:" name="contract">
  154. <picker
  155. @change="changeContract"
  156. :range="contracts"
  157. :value="formData.contract"
  158. >
  159. <view class="select">{{ contracts[formData.contract] }}</view>
  160. </picker>
  161. </uni-forms-item>
  162. </uni-forms>
  163. </uni-popup-dialog>
  164. </uni-popup>
  165. <uni-popup ref="wtyPopup" type="dialog">
  166. <uni-popup-dialog
  167. title="转质保"
  168. type="info"
  169. @confirm="submitWarranty"
  170. @close="onCancel"
  171. before-close
  172. >
  173. <uni-forms
  174. ref="wty"
  175. :modelValue="formData"
  176. label-position="left"
  177. :rules="wtyRules"
  178. >
  179. <uni-forms-item required label="质保经理:" name="manager">
  180. <uni-data-picker
  181. class="depSelect"
  182. placeholder="请选择质保经理"
  183. :localdata="depUserTree"
  184. @change="changeManager"
  185. />
  186. </uni-forms-item>
  187. </uni-forms>
  188. </uni-popup-dialog>
  189. </uni-popup>
  190. <uni-popup ref="optPopup" type="dialog">
  191. <uni-popup-dialog
  192. title="转运营"
  193. type="info"
  194. @confirm="submitOperate"
  195. @close="onCancel"
  196. before-close
  197. >
  198. <uni-forms
  199. ref="opt"
  200. :modelValue="formData"
  201. label-position="left"
  202. :rules="optRules"
  203. >
  204. <uni-forms-item required label="运营经理:" name="manager">
  205. <uni-data-picker
  206. class="depSelect"
  207. placeholder="请选择运营经理"
  208. :localdata="depUserTree"
  209. @change="changeManager"
  210. />
  211. </uni-forms-item>
  212. </uni-forms>
  213. </uni-popup-dialog>
  214. </uni-popup>
  215. </view>
  216. </template>
  217. <script>
  218. import { mapState } from "vuex";
  219. import {
  220. queryFlow,
  221. deleteApproval,
  222. submitAudit,
  223. queryUserDetail,
  224. authApproval,
  225. startExecution,
  226. startWarranty,
  227. startOperate,
  228. } from "@/services/project";
  229. import uniFormsItem from "../../uni_modules/uni-forms/components/uni-forms-item/uni-forms-item.vue";
  230. const contracts = ["无合同", "有合同"];
  231. export default {
  232. components: { uniFormsItem },
  233. data() {
  234. return {
  235. auth: false,
  236. status: ["售前", "转执行", "转运营", "转质保"],
  237. flowList: [],
  238. depRole: [],
  239. user: {},
  240. manager: "",
  241. contracts,
  242. formData: {
  243. manager: "",
  244. contract: null,
  245. },
  246. exeRules: {
  247. manager: {
  248. rules: [{ required: true, errorMessage: "请选择质保经理" }],
  249. },
  250. contract: {
  251. rules: [{ required: true, errorMessage: "请选择合同状态" }],
  252. },
  253. },
  254. wtyRules: {
  255. manager: {
  256. rules: [{ required: true, errorMessage: "请选择质保经理" }],
  257. },
  258. },
  259. optRules: {
  260. manager: {
  261. rules: [{ required: true, errorMessage: "请选择运营经理" }],
  262. },
  263. },
  264. };
  265. },
  266. computed: {
  267. ...mapState(["currentProject", "depUserTree"]),
  268. nodeList() {
  269. if (!this.flowList) return [];
  270. let flowInfo = this.flowList.find(
  271. (item) => item.id == this.currentProject.flow_id
  272. );
  273. if (!flowInfo) return [];
  274. let currentIndex = flowInfo.Nodes.findIndex(
  275. (item) => item.id == this.currentProject.node_id
  276. );
  277. return {
  278. currentIndex,
  279. list: flowInfo.Nodes.map((item) => ({
  280. title: item.node,
  281. desc: `审批人:${this.getAudits(item)}`,
  282. })),
  283. };
  284. },
  285. },
  286. onLoad(options) {
  287. this.auth = Boolean(options.auth);
  288. this.init();
  289. },
  290. methods: {
  291. async init() {
  292. this.user = uni.getStorageSync("user");
  293. let res;
  294. res = await queryFlow();
  295. this.flowList = res.data;
  296. res = await queryUserDetail(this.user);
  297. let depId = this.user.DepId;
  298. let dep = res.data.Dep.find((item) => item.ID == depId);
  299. this.depRole = dep.Role;
  300. },
  301. canAuth() {
  302. let { NodeInfo, audit_status, project_status } = this.currentProject;
  303. if (!NodeInfo || this.flowList.length == 0 || this.depRole.length == 0)
  304. return false;
  305. if (audit_status != 1) return false;
  306. if (project_status == 2)
  307. return this.currentProject.opt_manager_id == this.user.ID;
  308. if (project_status == 3)
  309. return this.currentProject.wty_manager_id == this.user.ID;
  310. let flow = this.flowList.find((item) => item.id == NodeInfo.flow_id);
  311. if (!flow) return false;
  312. let { NodeAudits } = flow.Nodes.find((item) => item.id == NodeInfo.id);
  313. const role = this.depRole.find((item) =>
  314. NodeAudits.find((audit) => audit.audit_role == item.ID)
  315. );
  316. return Boolean(role);
  317. },
  318. onHandleAudit(value) {
  319. if (value) {
  320. uni.showModal({
  321. title: "审批通过",
  322. content: "是否确认通过审批",
  323. confirmText: "通过",
  324. success: async (res) => {
  325. if (res.confirm) {
  326. let payload = {
  327. id: this.currentProject.id,
  328. project_full_code: this.currentProject.project_full_code,
  329. flow_id: this.currentProject.flow_id,
  330. node_id: this.currentProject.node_id,
  331. audit_status: 3,
  332. audit_comment: "",
  333. };
  334. await authApproval(payload);
  335. uni.showToast({
  336. title: "审核成功",
  337. });
  338. setTimeout(function () {
  339. uni.hideToast();
  340. uni.redirectTo({
  341. url: "./auth",
  342. });
  343. }, 1800);
  344. }
  345. },
  346. });
  347. } else {
  348. uni.showModal({
  349. title: "是否确认拒绝",
  350. content: "拒绝理由",
  351. editable: true,
  352. success: async (res) => {
  353. if (res.confirm) {
  354. let payload = {
  355. id: this.currentProject.id,
  356. project_full_code: this.currentProject.project_full_code,
  357. flow_id: this.currentProject.flow_id,
  358. node_id: this.currentProject.node_id,
  359. audit_status: 2,
  360. audit_comment: res.content,
  361. };
  362. await authApproval(payload);
  363. uni.showToast({
  364. title: "已拒绝",
  365. });
  366. setTimeout(function () {
  367. uni.hideToast();
  368. uni.redirectTo({
  369. url: "./auth",
  370. });
  371. }, 1800);
  372. }
  373. },
  374. });
  375. }
  376. },
  377. getAudits(nodeInfo) {
  378. switch (nodeInfo.id) {
  379. case 11:
  380. return "执行项目经理";
  381. case 12:
  382. return "运营经理";
  383. case 13:
  384. return "执行项目经理";
  385. case 14:
  386. return "质保经理";
  387. default:
  388. return (nodeInfo.NodeAudits || [])
  389. .map((item) => item.AuthorRoleInfo.Name)
  390. .join(",");
  391. }
  392. },
  393. canEdit(index) {
  394. let {
  395. audit_status,
  396. project_status,
  397. author,
  398. LeaderId,
  399. opt_manager_id,
  400. wty_manager_id,
  401. } = this.currentProject;
  402. //audit_status: 0未提审1审核中2审核拒绝3审核通过
  403. //project_status: 0售前1执行2转运营3转质保
  404. switch (index) {
  405. //编辑删除提审
  406. case 0:
  407. //售前阶段,未提审/审核被拒,创建人/管理员
  408. return (
  409. project_status == 0 &&
  410. (audit_status == 0 || audit_status == 2) &&
  411. (this.user.ID == author || this.user.IsSuper)
  412. );
  413. //成员管理
  414. case 1:
  415. //售前/执行/运营/质保,审核通过,项目经理/管理员
  416. let manager;
  417. switch (project_status) {
  418. case 0:
  419. manager == author;
  420. break;
  421. case 1:
  422. manager == LeaderId;
  423. break;
  424. case 2:
  425. manager == LeaderId || manager == opt_manager_id;
  426. break;
  427. case 3:
  428. manager == LeaderId || manager == wty_manager_id;
  429. break;
  430. }
  431. return (
  432. audit_status == 3 && (this.user.ID == manager || this.user.IsSuper)
  433. );
  434. //转执行
  435. case 2:
  436. //售前,审核通过,售前经理/管理员
  437. return (
  438. project_status == 0 &&
  439. audit_status == 3 &&
  440. (this.user.ID == author || this.user.IsSuper)
  441. );
  442. //转质保运营
  443. case 3:
  444. //执行,审核通过,执行经理/管理员
  445. return (
  446. project_status == 1 &&
  447. audit_status == 3 &&
  448. (this.user.ID == LeaderId || this.user.IsSuper)
  449. );
  450. }
  451. },
  452. onEdit() {
  453. uni.redirectTo({
  454. url: `./add?project_id=${this.currentProject.id}`,
  455. });
  456. },
  457. onDelete() {
  458. uni.showModal({
  459. title: "删除项目",
  460. content: "是否确认删除该项目",
  461. confirmText: "删除",
  462. confirmColor: "#ff7875",
  463. success: async (res) => {
  464. if (res.confirm) {
  465. await deleteApproval(this.currentProject);
  466. uni.showToast({
  467. title: "删除成功",
  468. });
  469. setTimeout(function () {
  470. uni.hideToast();
  471. uni.redirectTo({
  472. url: "./list",
  473. });
  474. }, 1800);
  475. }
  476. },
  477. });
  478. },
  479. onSubmitAuth() {
  480. uni.showModal({
  481. title: "提交审核",
  482. content: "是否确认提交审核",
  483. confirmText: "提审",
  484. success: async (res) => {
  485. if (res.confirm) {
  486. let payload = {
  487. id: this.currentProject.id,
  488. flow_id: this.currentProject.flow_id,
  489. node_id: this.currentProject.node_id,
  490. };
  491. await submitAudit(payload);
  492. uni.showToast({
  493. title: "提审成功",
  494. });
  495. setTimeout(function () {
  496. uni.hideToast();
  497. uni.redirectTo({
  498. url: "./list",
  499. });
  500. }, 1800);
  501. }
  502. },
  503. });
  504. },
  505. onMember() {
  506. uni.navigateTo({
  507. url: "./member",
  508. });
  509. },
  510. changeManager(e) {
  511. if (e.detail.value.length > 0)
  512. this.formData.manager = e.detail.value[e.detail.value.length - 1].value;
  513. else this.formData.manager = "";
  514. },
  515. onExecute() {
  516. this.$refs.exePopup.open();
  517. },
  518. changeContract(e) {
  519. this.formData.contract = e.detail.value;
  520. },
  521. async submitExecute() {
  522. this.$refs.exe.validate(async (err) => {
  523. if (!err) {
  524. const [dep_id, manager_id] = this.formData.manager.split("-");
  525. let payload = {
  526. project_code_id: this.currentProject.id,
  527. with_contract: Number(this.formData.contract),
  528. dep_id: Number(dep_id),
  529. exe_manager_id: Number(manager_id),
  530. };
  531. await startExecution(payload);
  532. this.$refs.exePopup.close();
  533. uni.showToast({
  534. title: "转执行送审成功",
  535. });
  536. setTimeout(function () {
  537. uni.hideToast();
  538. uni.redirectTo({
  539. url: "./list",
  540. });
  541. }, 1800);
  542. }
  543. });
  544. },
  545. onWarranty() {
  546. this.$refs.wtyPopup.open();
  547. },
  548. async submitWarranty() {
  549. this.$refs.wty.validate(async (err) => {
  550. if (!err) {
  551. const [dep_id, manager_id] = this.formData.manager.split("-");
  552. let payload = {
  553. project_code_id: this.currentProject.id,
  554. dep_id: Number(dep_id),
  555. wty_manager_id: Number(manager_id),
  556. };
  557. await startWarranty(payload);
  558. this.$refs.wtyPopup.close();
  559. uni.showToast({
  560. title: "转质保送审成功",
  561. });
  562. setTimeout(function () {
  563. uni.hideToast();
  564. uni.redirectTo({
  565. url: "./list",
  566. });
  567. }, 1800);
  568. }
  569. });
  570. },
  571. onOperate() {
  572. this.$refs.optPopup.open();
  573. },
  574. async submitOperate() {
  575. this.$refs.opt.validate(async (err) => {
  576. if (!err) {
  577. const [dep_id, manager_id] = this.formData.manager.split("-");
  578. let payload = {
  579. project_code_id: this.currentProject.id,
  580. dep_id: Number(dep_id),
  581. opt_manager_id: Number(manager_id),
  582. };
  583. await startOperate(payload);
  584. this.$refs.optPopup.close();
  585. uni.showToast({
  586. title: "转运营送审成功",
  587. });
  588. setTimeout(function () {
  589. uni.hideToast();
  590. uni.redirectTo({
  591. url: "./list",
  592. });
  593. }, 1800);
  594. }
  595. });
  596. },
  597. onCancel() {
  598. this.$refs.exePopup.close();
  599. this.$refs.wtyPopup.close();
  600. this.$refs.optPopup.close();
  601. this.formData = {
  602. manager: "",
  603. contract: null,
  604. };
  605. },
  606. },
  607. };
  608. </script>
  609. <style lang="less" scoped>
  610. .content {
  611. display: flex;
  612. flex-wrap: wrap;
  613. }
  614. .main {
  615. width: 100%;
  616. padding-bottom: 60px;
  617. }
  618. .title {
  619. width: 100%;
  620. padding: 0 20px 20px 20px;
  621. font: 24px bold;
  622. }
  623. .list {
  624. margin: 0 10% 20px 10%;
  625. }
  626. .detail {
  627. width: 100%;
  628. padding: 15px 0;
  629. font-size: 18px;
  630. display: flex;
  631. justify-items: space-between;
  632. .subTitle {
  633. width: 30%;
  634. }
  635. .detailContent {
  636. width: 70%;
  637. }
  638. }
  639. .select {
  640. width: 100%;
  641. height: 72rpx;
  642. line-height: 70rpx;
  643. border: 1px solid #666;
  644. padding-left: 20rpx;
  645. }
  646. ::v-deep {
  647. .uni-steps__column-title {
  648. font-size: 18px;
  649. line-height: 24px;
  650. }
  651. .uni-steps__column-desc {
  652. font-size: 14px;
  653. line-height: 18px;
  654. }
  655. }
  656. .editBtns {
  657. display: flex;
  658. justify-content: space-around;
  659. flex-wrap: wrap;
  660. margin: 0 5%;
  661. .edit {
  662. width: 30%;
  663. margin-bottom: 20px;
  664. font-size: 16px;
  665. text-align: center;
  666. }
  667. }
  668. .group {
  669. width: 100%;
  670. display: flex;
  671. position: fixed;
  672. flex-wrap: wrap;
  673. justify-content: flex-start;
  674. bottom: 0;
  675. left: 0;
  676. .commit {
  677. width: 50%;
  678. border-radius: 0;
  679. margin: inherit;
  680. }
  681. }
  682. .depSelect {
  683. width: 200px;
  684. }
  685. </style>