edit.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. <template>
  2. <div class="container">
  3. <el-form :model="form" label-position="top" :rules="rules" ref="form" label-width="80px">
  4. <el-form-item label="报销人">
  5. <el-input v-model="form.employeeName" disabled></el-input>
  6. </el-form-item>
  7. <el-form-item label="部门">
  8. <el-input v-model="form.deptName" disabled></el-input>
  9. </el-form-item>
  10. <el-form-item label="职位">
  11. <el-input v-model="form.position" disabled></el-input>
  12. </el-form-item>
  13. <el-form-item label="手机号">
  14. <el-input v-model="form.employeePhone" disabled></el-input>
  15. </el-form-item>
  16. <el-form-item label="报销类型" prop="expenseTypeId">
  17. <el-select v-model="form.expenseTypeId" placeholder="请选择" style="width: 100%;">
  18. <el-option v-for="item in posOptions" :key="item.value" :label="item.label"
  19. :value="item.value">
  20. </el-option>
  21. </el-select>
  22. </el-form-item>
  23. <el-form-item label="费用所属期间" prop="expenseMonth">
  24. <el-date-picker value-format="yyyy-MM" v-model="form.expenseMonth" type="month" placeholder="选择日期"
  25. style="width: 100%;">
  26. </el-date-picker>
  27. </el-form-item>
  28. <el-button style="margin-bottom: 10px;" type="success" size="small" @click="addRow">新增</el-button>
  29. <!--表格里面嵌套表单-->
  30. <el-table border :header-cell-style="{ 'text-align': 'center' }" :cell-style="{ 'text-align': 'center' }"
  31. :data="form.oaExpenseObj" ref="table" style="width: 100%">
  32. <el-table-column label="费用项目" width="300">
  33. <template slot-scope="scope">
  34. <el-form-item :prop="'oaExpenseObj.' + scope.$index + '.expenseItemId'"
  35. :rules="{ required: true, message: '费用项目不能为空', trigger: 'blur' }">
  36. <el-select v-model="form.oaExpenseObj[scope.$index].expenseItemId" placeholder="请选择费用项目" style="width: 100%;" size="small">
  37. <el-option v-for="item in posOptions" :key="item.value" :label="item.label"
  38. :value="item.value">
  39. </el-option>
  40. </el-select>
  41. </el-form-item>
  42. </template>
  43. </el-table-column>
  44. <el-table-column label="金额(元)" width="200">
  45. <template slot-scope="scope">
  46. <el-form-item :prop="'oaExpenseObj.' + scope.$index + '.money'"
  47. :rules="{ required: true, message: '金额不能为空', trigger: 'blur' }">
  48. <el-input v-model="form.oaExpenseObj[scope.$index].money" autocomplete="off" size="small"
  49. placeholder="金额" oninput="value=value.match(/\d+\.?\d{0,2}/,'')" @input="changeMoney(scope.$index)"></el-input>
  50. </el-form-item>
  51. </template>
  52. </el-table-column>
  53. <el-table-column label="开始日期" width="200">
  54. <template slot-scope="scope">
  55. <el-form-item :prop="'oaExpenseObj.' + scope.$index + '.startDate'"
  56. :rules="{ required: true, message: '开始日期不能为空', trigger: 'blur' }">
  57. <el-date-picker value-format="yyyy-MM-dd" v-model="form.oaExpenseObj[scope.$index].startDate" type="date" placeholder="选择日期"
  58. style="width: 100%;">
  59. </el-date-picker>
  60. </el-form-item>
  61. </template>
  62. </el-table-column>
  63. <el-table-column label="结束日期" width="200">
  64. <template slot-scope="scope">
  65. <el-form-item :prop="'oaExpenseObj.' + scope.$index + '.endDate'"
  66. :rules="{ required: true, message: '结束日期不能为空', trigger: 'blur' }">
  67. <el-date-picker value-format="yyyy-MM-dd" v-model="form.oaExpenseObj[scope.$index].endDate" type="date" placeholder="选择日期"
  68. style="width: 100%;">
  69. </el-date-picker>
  70. </el-form-item>
  71. </template>
  72. </el-table-column>
  73. <el-table-column label="说明" width="500">
  74. <template slot-scope="scope">
  75. <el-form-item :prop="'oaExpenseObj.' + scope.$index + '.remarks'"
  76. :rules="{ required: true, message: '说明不能为空', trigger: 'blur' }">
  77. <el-input maxlength="20" v-model="form.oaExpenseObj[scope.$index].remarks" autocomplete="off" size="small"
  78. placeholder="说明名称"></el-input>
  79. </el-form-item>
  80. </template>
  81. </el-table-column>
  82. <el-table-column fixed="right" label="操作" width="80">
  83. <template slot-scope="scope">
  84. <el-button :disabled="form.oaExpenseObj.length > 1 ? false : true" style="margin-bottom: 22px;" @click="handleDeleteRow(scope.$index)"
  85. type="text" size="small">删除</el-button>
  86. </template>
  87. </el-table-column>
  88. </el-table>
  89. <el-form-item label="报销总金额(元)" prop="totalMoney">
  90. <el-input type="number" v-model="form.totalMoney" placeholder='报销总金额' disabled></el-input>
  91. </el-form-item>
  92. <el-form-item label="附件">
  93. <el-upload :action="fileUrl" :headers="headers" :file-list="files" :on-success="handleFileSuccessCite"
  94. :before-upload="beforeUploadFile" :on-remove="handleRemove">
  95. <el-button size="small" type="primary">点击上传</el-button>
  96. </el-upload>
  97. </el-form-item>
  98. <el-form-item label="备注">
  99. <el-input maxlength="200" rows="4" show-word-limit type="textarea" v-model="form.remarks" placeholder="请输入备注" />
  100. </el-form-item>
  101. <el-form-item label="审批人" prop="peopleList">
  102. <el-input v-model="form.peopleList" style="display: none;" />
  103. <el-button class="button-new-tag" @click="openPS">+ 添加</el-button>
  104. <span v-for="(tag, index) in nikeNamelist" :key="index">
  105. <i class="el-icon-arrow-right"></i>
  106. <el-tag type="info" closable :disable-transitions="false" @close="handleClose(index)">
  107. <span class="user-avatar">{{ tag.substring(0, 1) || 'U' }}</span>
  108. {{ tag }}
  109. </el-tag>
  110. </span>
  111. </el-form-item>
  112. <el-form-item label="流程动态" v-if="tasks.length > 0">
  113. <el-timeline>
  114. <el-timeline-item v-for="(item, index) in tasks" :key="index" :icon="getTimelineItemIcon(item)"
  115. :type="getTimelineItemType(item)">
  116. <p style="font-weight: 700">任务:{{ item.name }}</p>
  117. <el-card :body-style="{ padding: '10px' }">
  118. <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px;">
  119. 审批人:{{ item.assigneeUser.nickname }}
  120. <el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag>
  121. </label>
  122. <label style="font-weight: normal" v-if="item.createTime">创建时间:</label>
  123. <label style="color:#8a909c; font-weight: normal">{{ parseTime(item.createTime) }}</label>
  124. <label v-if="item.endTime" style="margin-left: 30px;font-weight: normal">审批时间:</label>
  125. <label v-if="item.endTime" style="color:#8a909c;font-weight: normal"> {{
  126. parseTime(item.endTime)
  127. }}</label>
  128. <!-- <label v-if="item.durationInMillis" style="margin-left: 30px;font-weight: normal">耗时:</label>
  129. <label v-if="item.durationInMillis" style="color:#8a909c;font-weight: normal">
  130. {{ getDateStar(item.durationInMillis) }} </label> -->
  131. <p v-if="item.reason">
  132. <el-tag :type="getTimelineItemType(item)">{{ item.reason }}</el-tag>
  133. </p>
  134. </el-card>
  135. </el-timeline-item>
  136. </el-timeline>
  137. </el-form-item>
  138. <el-form-item>
  139. <div v-if="name == 'todo'">
  140. <el-button type="primary" @click="onReCommit()" v-loading.fullscreen.lock="fullscreenLoading">提交</el-button>
  141. <el-button type="warning" @click="onClose()" v-loading.fullscreen.lock="fullscreenLoading">关闭</el-button>
  142. </div>
  143. <div v-else>
  144. <el-button type="primary" @click="onSubmit()" v-loading.fullscreen.lock="fullscreenLoading">提交</el-button>
  145. <el-button @click="onSave()" v-loading.fullscreen.lock="fullscreenLoading">暂存</el-button>
  146. <el-button v-if="form.auditStatus == 0" type="danger" @click="onDelete()"
  147. v-loading.fullscreen.lock="fullscreenLoading">删除</el-button>
  148. </div>
  149. </el-form-item>
  150. </el-form>
  151. <PeopleSelect ref="peopleSelect" :type="type" :isCheck="true" :open="peopleOpen" @cancel="peopleOpen = false"
  152. @submit="submitPeople"></PeopleSelect>
  153. </div>
  154. </template>
  155. <script>
  156. import { getDetail, create, reCommit, save, deleteById, closeById } from "@/api/oa/expense"
  157. import { getDate } from "@/utils/dateUtils";
  158. import { getUserProfile } from "@/api/system/user";
  159. import { listDept } from "@/api/system/dept";
  160. import PeopleSelect from "@/components/PeopleSelect/index.vue";
  161. import Treeselect from "@riophae/vue-treeselect";
  162. import "@riophae/vue-treeselect/dist/vue-treeselect.css";
  163. import { getBaseHeader } from "@/utils/request";
  164. export default {
  165. name: 'Index',
  166. components: {
  167. PeopleSelect,
  168. Treeselect,
  169. },
  170. props: {
  171. id: {
  172. type: [String, Number],
  173. default: undefined
  174. },
  175. name: {
  176. type: String,
  177. default: undefined
  178. },
  179. },
  180. data() {
  181. return {
  182. fullscreenLoading: false,
  183. tasks: [],
  184. loading: false,
  185. files: [],
  186. // 设置上传的请求头部
  187. headers: getBaseHeader(),
  188. // 上传的地址
  189. fileUrl: process.env.VUE_APP_BASE_API + '/admin-api/infra/file/uploaData',
  190. // fileUrl: 'http://192.168.100.249:48080/admin-api/infra/file/uploaData',
  191. // 部门树选项
  192. posOptions: [],
  193. deptOptions: [],
  194. nikeNamelist: [],
  195. form: {
  196. employeeName: undefined,
  197. deptName: undefined,
  198. position: undefined,
  199. employeePhone: undefined,
  200. expenseTypeId: undefined,
  201. expenseMonth: undefined,
  202. oaExpenseObj: [],
  203. totalMoney: undefined,
  204. fileIdList: undefined,
  205. remarks: undefined,
  206. peopleList: '',
  207. },
  208. rules: {
  209. expenseTypeId: [
  210. { required: true, message: '请选择报销类型', trigger: 'change' }
  211. ],
  212. expenseMonth: [
  213. { required: true, message: '请选择费用所属期间', trigger: 'change' }
  214. ],
  215. totalMoney: [
  216. { required: true, message: '请输入报销总金额', trigger: 'blur' }
  217. ],
  218. peopleList: [
  219. { required: true, message: '请选择审批人', }
  220. ]
  221. },
  222. status: true,
  223. type: 'multiple',
  224. //是否打开选人组件,默认不打开
  225. peopleOpen: false,
  226. queryParams: {
  227. name: undefined,
  228. status: undefined
  229. },
  230. }
  231. },
  232. created() {
  233. this.getDeptList();
  234. },
  235. watch: {
  236. id: {
  237. immediate: true,
  238. handler(val) {
  239. if (val) {
  240. this.getDetail(val);
  241. } else {
  242. this.getUser();
  243. this.addRow();
  244. }
  245. }
  246. }
  247. },
  248. methods: {
  249. changeMoney(index){
  250. if(this.form.oaExpenseObj[index].money){
  251. let money = this.form.oaExpenseObj[index].money;
  252. this.form.oaExpenseObj[index].money = money;
  253. let arr = this.form.oaExpenseObj;
  254. this.form.totalMoney = this.sum(arr);
  255. }
  256. },
  257. //求和
  258. sum(arr){
  259. return arr.reduce((prev,cur)=>{
  260. return ((prev* 100) + (cur.money * 100))/100
  261. },0)
  262. },
  263. /**
  264. * 新增行
  265. */
  266. addRow() {
  267. this.form.oaExpenseObj.push({
  268. expenseItemId: '',
  269. money: '',
  270. startDate: '',
  271. endDate: '',
  272. remarks: '',
  273. });
  274. },
  275. /**
  276. * 删除行
  277. * @param row
  278. */
  279. handleDeleteRow(index) {
  280. this.form.oaExpenseObj.splice(index, 1);
  281. let arr = this.form.oaExpenseObj;
  282. this.form.totalMoney = this.sum(arr);
  283. },
  284. getDateStar(ms) {
  285. return getDate(ms);
  286. },
  287. getUser() {
  288. getUserProfile().then(response => {
  289. let userInfo = response.data;
  290. this.form.employeeName = userInfo.nickname;
  291. this.form.deptName = userInfo.dept.name;
  292. this.form.position = userInfo.posts[0].name;
  293. this.form.employeePhone = userInfo.mobile;
  294. });
  295. },
  296. handleRemove(file, fileList) {
  297. console.log(file, fileList);
  298. let fileIds = [];
  299. for (let i in fileList) {
  300. let id = fileList[i].response.data.id;
  301. fileIds.push(id);
  302. }
  303. this.form.fileIdList = fileIds;
  304. },
  305. beforeUploadFile(file) {
  306. console.log(file);
  307. const size = file.size / 1024 / 1024;
  308. console.log(size);
  309. if (size > 5) {
  310. this.$message.error("文件大小不能超过5MB!");
  311. return false;
  312. }
  313. },
  314. handleFileSuccessCite(res, file, fileList) {
  315. console.log(file, fileList);
  316. console.log("------", "==========");
  317. console.log("res = ", res);
  318. let fileIds = [];
  319. for (let i in fileList) {
  320. let response = fileList[i].response;
  321. if (response.errno && response.errno != "0") {
  322. this.$message.error("该文件上传失败,已被移除,请重新上传!");
  323. // 上传失败移除该 file 对象
  324. fileList.splice(i, 1);
  325. } else {
  326. let id = fileList[i].response.data.id;
  327. fileIds.push(id);
  328. }
  329. }
  330. this.form.fileIdList = fileIds;
  331. },
  332. /** 获得表单信息 */
  333. getDetail(val) {
  334. this.$parent.$parent.detailLoading = true;
  335. getDetail(val).then(response => {
  336. this.$parent.$parent.detailLoading = false;
  337. this.form = response.data;
  338. let files = response.data.fileList;
  339. if (files) {
  340. this.files = [];
  341. for (let i in files) {
  342. let url = files[i].url;
  343. let name = files[i].name;
  344. let id = files[i].id;
  345. this.files.push({
  346. name: name,
  347. url: url,
  348. response: { error: "0", data: { url: url, id: id } },
  349. });
  350. }
  351. }
  352. if (response.data.auditUserList) {
  353. let auditUserList = response.data.auditUserList;
  354. let peopleList = [];
  355. let nikeNamelist = [];
  356. auditUserList.map(item => {
  357. peopleList.push(item.id);
  358. nikeNamelist.push(item.nickname)
  359. });
  360. this.$set(this.form, 'peopleList', peopleList.join(','));
  361. this.nikeNamelist = nikeNamelist;
  362. } else {
  363. this.$set(this.form, 'peopleList', '');
  364. this.nikeNamelist = [];
  365. }
  366. this.tasks = response.data.auditRecordList ? response.data.auditRecordList : [];
  367. });
  368. },
  369. /** 查询部门列表 */
  370. getDeptList() {
  371. listDept(this.queryParams).then(response => {
  372. this.deptOptions = this.handleTree(response.data, "id");
  373. });
  374. },
  375. /** 转换部门数据结构 */
  376. normalizer(node) {
  377. if (node.children && !node.children.length) {
  378. delete node.children;
  379. }
  380. return {
  381. id: node.id,
  382. label: node.name,
  383. children: node.children
  384. };
  385. },
  386. //提交
  387. onSubmit() {
  388. console.log(this.form);
  389. this.$refs.form.validate(valid => {
  390. if (valid) {
  391. console.log(this.form);
  392. this.form.startUserSelectAssignees = this.form.peopleList.split(',');
  393. this.form.auditPass = true;
  394. this.fullscreenLoading = true;
  395. create(this.form).then(response => {
  396. this.fullscreenLoading = false;
  397. this.$modal.msgSuccess("提交成功");
  398. if (this.id) {
  399. this.$parent.$parent.closeEdit();
  400. } else {
  401. this.$parent.$parent.setStatus(2);
  402. }
  403. });
  404. } else {
  405. console.log('error submit!!');
  406. return false;
  407. }
  408. });
  409. },
  410. //驳回或撤回后再次提交通用用事项审批流程信息
  411. onReCommit() {
  412. console.log(this.form);
  413. this.$refs.form.validate(valid => {
  414. if (valid) {
  415. console.log(this.form);
  416. this.form.startUserSelectAssignees = this.form.peopleList.split(',');
  417. this.form.auditPass = true;
  418. this.fullscreenLoading = true;
  419. reCommit(this.form).then(response => {
  420. this.fullscreenLoading = false;
  421. this.$modal.msgSuccess("提交成功");
  422. if (this.id) {
  423. this.$parent.$parent.closeEdit();
  424. } else {
  425. this.$parent.$parent.setStatus(2);
  426. }
  427. });
  428. } else {
  429. console.log('error submit!!');
  430. return false;
  431. }
  432. });
  433. },
  434. //暂存
  435. onSave() {
  436. if (this.form.peopleList) {
  437. this.form.startUserSelectAssignees = this.form.peopleList.split(',');
  438. }
  439. this.form.auditPass = false;
  440. this.fullscreenLoading = true;
  441. save(this.form).then(response => {
  442. this.fullscreenLoading = false;
  443. this.$modal.msgSuccess("暂存成功");
  444. if (this.id) {
  445. this.$parent.$parent.closeEdit();
  446. } else {
  447. this.$parent.$parent.setStatus(2);
  448. }
  449. });
  450. },
  451. //暂存删除
  452. async onDelete() {
  453. this.$modal.confirm('是否确认删除?').then(() => {
  454. this.fullscreenLoading = true;
  455. deleteById(this.id).then(response => {
  456. this.fullscreenLoading = false;
  457. this.$modal.msgSuccess("删除成功");
  458. this.$parent.$parent.closeEdit();
  459. })
  460. }).catch(() => { });
  461. },
  462. //暂存删除
  463. async onClose() {
  464. this.$modal.confirm('是否确认关闭?').then(() => {
  465. this.fullscreenLoading = true;
  466. closeById(this.id).then(response => {
  467. this.fullscreenLoading = false;
  468. this.$modal.msgSuccess("流程已关闭");
  469. this.$parent.$parent.closeEdit();
  470. })
  471. }).catch(() => { });
  472. },
  473. // 关闭标签
  474. handleClose(index) {
  475. this.nikeNamelist.splice(index, 1);
  476. let peopleList = this.form.peopleList.split(',');
  477. peopleList.splice(index, 1);
  478. this.form.peopleList = peopleList.join(',');
  479. },
  480. //打开选人弹窗
  481. openPS() {
  482. this.peopleOpen = true;
  483. },
  484. //选择人的确定按钮事件 submitPeople(nikeNamelist)方法传参一个默认接收用户昵称数组 submitPeople(peopleList,nikeNamelist)方法传参两个则是接收用户昵称数组和用户账号数组
  485. submitPeople(userNamelist, nikeNamelist, userIdList) {
  486. console.log(userNamelist);
  487. this.nikeNamelist = nikeNamelist;
  488. this.form.peopleList = userIdList.join(',');
  489. this.peopleOpen = false;
  490. },
  491. getTimelineItemIcon(item) {
  492. if (item.result === 1) {
  493. return 'el-icon-time';
  494. }
  495. if (item.result === 2) {
  496. return 'el-icon-check';
  497. }
  498. if (item.result === 3) {
  499. return 'el-icon-close';
  500. }
  501. if (item.result === 4) {
  502. return 'el-icon-remove-outline';
  503. }
  504. if (item.result === 5) {
  505. return 'el-icon-back'
  506. }
  507. return '';
  508. },
  509. getTimelineItemType(item) {
  510. if (item.result === 1) {
  511. return 'primary';
  512. }
  513. if (item.result === 2) {
  514. return 'success';
  515. }
  516. if (item.result === 3) {
  517. return 'danger';
  518. }
  519. if (item.result === 4) {
  520. return 'info';
  521. }
  522. if (item.result === 5) {
  523. return 'warning';
  524. }
  525. if (item.result === 6) {
  526. return 'default'
  527. }
  528. return '';
  529. },
  530. }
  531. }
  532. </script>
  533. <style lang="scss" scoped>
  534. .user-avatar {
  535. width: 22px;
  536. height: 22px;
  537. line-height: 19px;
  538. font-size: 12px;
  539. background: #46c26f;
  540. border: 1px solid transparent;
  541. border-radius: 50%;
  542. color: #fff;
  543. display: inline-block;
  544. overflow: hidden;
  545. text-align: center;
  546. vertical-align: middle;
  547. margin-bottom: 2px;
  548. }
  549. .el-tag+.el-tag {
  550. margin-left: 10px;
  551. }
  552. .button-new-tag {
  553. margin-right: 10px;
  554. height: 30px;
  555. line-height: 30px;
  556. padding-top: 0;
  557. padding-bottom: 0;
  558. }
  559. ul {
  560. padding-left: 0px !important;
  561. }
  562. .el-tag+.el-tag {
  563. margin-left: 10px;
  564. }
  565. </style>