applyAddWork.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801
  1. <template>
  2. <el-dialog
  3. :close-on-click-modal="false"
  4. :close-on-press-escape="false"
  5. :visible.sync="dialogVisible"
  6. title="加班申请单"
  7. top="50px"
  8. width="75%"
  9. :before-close="beforeClose"
  10. >
  11. <div class="tabsdom">
  12. <el-tabs v-model="activeName" @tab-click="handleClick">
  13. <el-tab-pane label="加班申请" name="first">
  14. <el-card shadow="always" style="padding: 15px 5px 5px 15px">
  15. <el-row :gutter="15">
  16. <el-form ref="elForm" :model="formData" :rules="rules" label-width="100px">
  17. <el-col :span="24">
  18. <el-form-item label="姓名">
  19. <el-input v-model="userinfo.truename" placeholder="请输入加班申请" readonly>
  20. </el-input>
  21. </el-form-item>
  22. </el-col>
  23. <el-col :span="12">
  24. <el-form-item label="所在部门">
  25. <el-input v-model="userinfo.deptName" placeholder="请输入所在部门" readonly>
  26. </el-input>
  27. </el-form-item>
  28. </el-col>
  29. <el-col :span="12">
  30. <el-form-item label="岗位">
  31. <el-input v-model="userinfo.postName" placeholder="请输入岗位" readonly>
  32. </el-input>
  33. </el-form-item>
  34. </el-col>
  35. <el-col :span="24">
  36. <el-form-item label="关联项目" prop="proId">
  37. <el-select v-model="formData.proId" filterable placeholder="关联项目名称">
  38. <el-option
  39. v-for="item in ProjectData"
  40. :key="item.value"
  41. :label="item.label"
  42. :value="item.value"
  43. >
  44. <span style="float: left">{{ item.label }}</span>
  45. <span style="float: right; color: #8492a6; font-size: 13px">{{
  46. item.custname
  47. }}</span>
  48. </el-option>
  49. </el-select>
  50. </el-form-item>
  51. </el-col>
  52. <el-col :span="24">
  53. <el-form-item label="加班事由" prop="applyReasons">
  54. <el-input v-model="formData.applyReasons" :autosize="{minRows: 4, maxRows: 4}"
  55. placeholder="请输入加班事由"
  56. type="textarea"
  57. ></el-input>
  58. </el-form-item>
  59. </el-col>
  60. <el-col :span="24">
  61. <el-form-item label="加班位置" prop="position">
  62. <el-radio-group v-model="formData.position" size="medium">
  63. <el-radio
  64. v-for="(item, index) in positionOptions"
  65. :key="index" :disabled="item.disabled"
  66. :label="item.value"
  67. >
  68. {{ item.label }}
  69. </el-radio>
  70. </el-radio-group>
  71. </el-form-item>
  72. </el-col>
  73. <el-col :span="24">
  74. <el-form-item label="加班时间">
  75. <el-row v-for="(item,index) in applyAddWorkTime">
  76. <el-col :span="10">
  77. <el-date-picker
  78. v-model="item.startDay"
  79. class="pdr10px mgb10px"
  80. placeholder="选择日期"
  81. type="date"
  82. value-format="yyyy-MM-dd"
  83. >
  84. </el-date-picker>
  85. </el-col>
  86. <el-col :span="10">
  87. <el-time-picker
  88. v-model="item.timeRange"
  89. :disabled="!item.startDay"
  90. class="el-input pdr10px mgb10px"
  91. clearable
  92. end-placeholder="结束时间"
  93. format="HH:mm"
  94. is-range
  95. range-separator="至"
  96. start-placeholder="开始时间"
  97. value-format="HH:mm"
  98. @change="getTimeRange($event,index)"
  99. />
  100. </el-col>
  101. <el-col :span="2">
  102. <div style="text-align: center">小计{{ item.useTime }}(h)</div>
  103. </el-col>
  104. <el-col :span="1" class="col-txt" style="text-align: center">
  105. &nbsp;
  106. <el-button
  107. v-if="index!=0"
  108. circle
  109. icon="el-icon-minus"
  110. @click="spliceListRow(index)"
  111. />
  112. </el-col>
  113. <el-col :span="1" class="col-txt" style="text-align: center">
  114. <el-button
  115. circle
  116. icon="el-icon-plus"
  117. type="primary"
  118. @click="addListRow()"
  119. />
  120. </el-col>
  121. </el-row>
  122. <div class="totalApplyTime">共计加班时长{{ formData.totalAddTime }}(h)</div>
  123. </el-form-item>
  124. </el-col>
  125. <el-col :span="24">
  126. <el-form-item label="备注">
  127. <el-input
  128. v-model="formData.remark"
  129. :autosize="{minRows: 4, maxRows: 4}"
  130. :style="{width: '100%'}"
  131. placeholder="请输入备注"
  132. type="textarea"
  133. ></el-input>
  134. </el-form-item>
  135. </el-col>
  136. </el-form>
  137. </el-row>
  138. </el-card>
  139. </el-tab-pane>
  140. <el-tab-pane label="流程图 " name="second">
  141. <div style="width: 100%">
  142. <el-row>
  143. <el-col :span="24">
  144. <div class="node_info">
  145. <div>节点说明:</div>
  146. <div class="dis_flex" v-for="item in nodeColor">
  147. <div class="node_class" :style="{backgroundColor: item.nodeback}"></div>
  148. {{ item.name }}
  149. </div>
  150. </div>
  151. <div id="containeraddwork" style="width: 100%" v-show="true"></div>
  152. </el-col>
  153. </el-row>
  154. </div>
  155. </el-tab-pane>
  156. </el-tabs>
  157. </div>
  158. <div slot="footer">
  159. <el-button @click="dialogVisible=false">取消</el-button>
  160. <el-button :loading="loading" type="primary" @click="handelConfirm">确定</el-button>
  161. </div>
  162. </el-dialog>
  163. </template>
  164. <script>
  165. import { upload } from '@/static/utils/channel'
  166. import Base from '@/views/base/base'
  167. import BaseData from '@/views/base/baseData'
  168. import UserSelect from '@/views/components/UserSelect'
  169. import * as echarts from 'echarts'
  170. const lineStyle = {
  171. color: '#00116a',
  172. width: 2
  173. }
  174. const redLinestyle = {
  175. color: 'red',
  176. width: 2
  177. }
  178. const applyAddWorkTime = { useTime: 0 }
  179. export default {
  180. name: 'ApplyPay',
  181. mixins: [Base, BaseData],
  182. components: {
  183. upload, UserSelect
  184. },
  185. data() {
  186. return {
  187. loading: false,
  188. nodeColor: [
  189. { name: '审核通过', nodeback: '#2A3980' },
  190. { name: '未经过', nodeback: '#999999' },
  191. { name: '退回', nodeback: '#E04242' },
  192. { name: '审核中', nodeback: '#E08E42' },
  193. { name: '撤回', nodeback: '#4294E0' }
  194. ],
  195. dc_key: ['ORDER_STATUS', 'PAY_TYPE', 'BUDGET_FY'],
  196. ProjectData: [],
  197. dialogVisible: false,
  198. applyAddWorkTime: [{ ...applyAddWorkTime }],
  199. formData: {
  200. proId: '',
  201. totalAddTime: 0,
  202. applyReasons: '',
  203. position: '',
  204. applyAddWorkTime: [
  205. {
  206. useTime: 0
  207. }
  208. ]
  209. },
  210. userinfo: {},
  211. activeName: 'first',
  212. rules: {
  213. proId: [{
  214. required: true,
  215. message: '请选择关联项目',
  216. trigger: 'change'
  217. }],
  218. applyReasons: [{
  219. required: true,
  220. message: '请输入加班事由',
  221. trigger: 'change'
  222. }],
  223. position: [{
  224. required: true,
  225. message: '加班位置不能为空',
  226. trigger: 'change'
  227. }]
  228. },
  229. positionOptions: [
  230. {
  231. 'label': '公司',
  232. 'value': 1
  233. },
  234. {
  235. 'label': '客户处',
  236. 'value': 2
  237. }, {
  238. 'label': '居家',
  239. 'value': 3
  240. }],
  241. nodes: [ // 节点
  242. {
  243. name: '申请人发起',
  244. value: [45, 100],
  245. symbol: 'image://' + require('../asste/huifangkuai.png'),
  246. symbolSize: [110, 60]
  247. },
  248. {
  249. name: '直接上级审核',
  250. value: [125, 100],
  251. symbol: 'image://' + require('../asste/huifangkuai.png'),
  252. symbolSize: [110, 60]
  253. },
  254. {
  255. name: '部门负责人审核',
  256. value: [205, 100],
  257. symbol: 'image://' + require('../asste/huifangkuai.png'),
  258. symbolSize: [110, 60]
  259. },
  260. {
  261. name: '分管领导审核',
  262. value: [285, 100],
  263. symbol: 'image://' + require('../asste/huifangkuai.png'),
  264. symbolSize: [110, 60]
  265. },
  266. {
  267. name: '上级分管领导\n审核',
  268. value: [365, 100],
  269. symbol: 'image://' + require('../asste/huifangkuai.png'),
  270. symbolSize: [110, 60]
  271. },
  272. {
  273. name: '主要领导\n审核',
  274. value: [445, 100],
  275. symbol: 'image://' + require('../asste/huifangkuai.png'),
  276. symbolSize: [110, 60]
  277. },
  278. {
  279. name: '结束',
  280. value: [525, 100],
  281. symbol: 'image://' + require('../asste/huifangkuai.png'),
  282. symbolSize: [110, 60]
  283. },
  284. {
  285. label: {
  286. show: true,
  287. color: 'red', // 节点文字颜色
  288. backgroundColor: '#f5f5f5'
  289. },
  290. itemStyle: {
  291. color: '#f5f5f5'
  292. },
  293. name: '退回发起人',
  294. value: [350, 400],
  295. symbolSize: [70, 20]
  296. },
  297. {
  298. label: {
  299. show: true,
  300. color: 'red', // 节点文字颜色
  301. backgroundColor: '#f5f5f5'
  302. },
  303. itemStyle: {
  304. color: '#f5f5f5'
  305. },
  306. name: ' 退回发起人 ',
  307. value: [300, 350],
  308. symbolSize: [20, 20]
  309. },
  310. {
  311. label: {
  312. show: true,
  313. color: 'red', // 节点文字颜色
  314. backgroundColor: '#f5f5f5'
  315. },
  316. itemStyle: {
  317. color: '#f5f5f5'
  318. },
  319. name: ' 退回发起人 ',
  320. value: [250, 300],
  321. symbolSize: [20, 20]
  322. },
  323. {
  324. label: {
  325. show: true,
  326. color: 'red', // 节点文字颜色
  327. backgroundColor: '#f5f5f5'
  328. },
  329. itemStyle: {
  330. color: '#f5f5f5'
  331. },
  332. name: ' 退回发起人 ',
  333. value: [250, 300],
  334. symbolSize: [20, 20]
  335. },
  336. {
  337. label: {
  338. show: true,
  339. color: 'red', // 节点文字颜色
  340. backgroundColor: '#f5f5f5'
  341. },
  342. itemStyle: {
  343. color: '#f5f5f5'
  344. },
  345. name: ' 退回发起人 ',
  346. value: [175, 250],
  347. symbolSize: [20, 20]
  348. },
  349. {
  350. label: {
  351. show: true,
  352. color: 'red', // 节点文字颜色
  353. backgroundColor: '#f5f5f5'
  354. },
  355. itemStyle: {
  356. color: '#f5f5f5'
  357. },
  358. name: ' 退回发起人 ',
  359. value: [100, 200],
  360. symbolSize: [20, 20]
  361. }
  362. ],
  363. linesData: [ // 连线
  364. {
  365. lineStyle: lineStyle,
  366. coords: [[45, 100], [105, 100]]
  367. },
  368. {
  369. lineStyle: lineStyle,
  370. coords: [[125, 100], [185, 100]]
  371. },
  372. {
  373. lineStyle: lineStyle,
  374. coords: [[205, 100], [265, 100]]
  375. },
  376. {
  377. lineStyle: lineStyle,
  378. coords: [[285, 100], [345, 100]]
  379. },
  380. {
  381. lineStyle: lineStyle,
  382. coords: [[365, 100], [425, 100]]
  383. },
  384. {
  385. lineStyle: lineStyle,
  386. coords: [[445, 100], [505, 100]]
  387. },
  388. {
  389. lineStyle: lineStyle,
  390. coords: [[450, 100], [450, 400]],
  391. symbol: 'none'
  392. },
  393. {
  394. lineStyle: lineStyle,
  395. coords: [[370, 100], [370, 350]],
  396. symbol: 'none'
  397. },
  398. {
  399. lineStyle: lineStyle,
  400. coords: [[290, 100], [290, 300]],
  401. symbol: 'none'
  402. },
  403. {
  404. lineStyle: lineStyle,
  405. coords: [[210, 100], [210, 250]],
  406. symbol: 'none'
  407. },
  408. {
  409. lineStyle: lineStyle,
  410. coords: [[130, 100], [130, 200]],
  411. symbol: 'none'
  412. },
  413. {
  414. lineStyle: lineStyle,
  415. coords: [[50, 100], [50, 400]],
  416. symbol: 'none'
  417. },
  418. {
  419. lineStyle: lineStyle,
  420. coords: [[450, 400], [50, 400]],
  421. symbol: 'none'
  422. },
  423. {
  424. lineStyle: lineStyle,
  425. coords: [[370, 350], [50, 350]],
  426. symbol: 'none'
  427. },
  428. {
  429. lineStyle: lineStyle,
  430. coords: [[290, 300], [50, 300]],
  431. symbol: 'none'
  432. },
  433. {
  434. lineStyle: lineStyle,
  435. coords: [[210, 250], [50, 250]],
  436. symbol: 'none'
  437. },
  438. {
  439. lineStyle: lineStyle,
  440. coords: [[130, 200], [50, 200]],
  441. symbol: 'none'
  442. },
  443. {
  444. lineStyle: lineStyle,
  445. coords: [[50, 150], [50, 150]],
  446. symbol: 'none'
  447. }
  448. ]
  449. }
  450. },
  451. computed: {},
  452. watch: {
  453. 'applyAddWorkTime': {
  454. deep: true,
  455. handler(newValue, oldValue) {
  456. let totalAddTime = 0
  457. console.log(this.applyAddWorkTime)
  458. try {
  459. for (let i = 0; i < this.applyAddWorkTime.length; i++) {
  460. if (this.applyAddWorkTime[i].useTime === 0 || this.applyAddWorkTime[i].useTime) {
  461. totalAddTime = totalAddTime + Number(this.applyAddWorkTime[i].useTime)
  462. }
  463. }
  464. console.log(totalAddTime)
  465. this.changeAddWorkTime(totalAddTime.toFixed(1))
  466. } catch (e) {
  467. console.log(e)
  468. }
  469. }
  470. }
  471. },
  472. created() {
  473. },
  474. mounted() {
  475. this.initDict(this.dc_key).then((res) => {
  476. })
  477. this.initProject({ /* signstatus: '2,3'*/ })
  478. },
  479. methods: {
  480. beforeClose() {
  481. this.applyAddWorkTime = [{ ...applyAddWorkTime }],
  482. this.formData = {
  483. proId: '',
  484. totalAddTime: 0,
  485. applyReasons: '',
  486. position: '',
  487. applyAddWorkTime: [
  488. {
  489. useTime: 0
  490. }
  491. ]
  492. }
  493. this.dialogVisible = false
  494. this.activeName = 'first'
  495. this.$forceUpdate()
  496. },
  497. handleClick(tab, event) {
  498. if (this.activeName == 'second') this.createNodeCanvas()
  499. },
  500. createNodeCanvas() {
  501. this.$nextTick(() => {
  502. let chartDom = document.getElementById('containeraddwork')
  503. var myCharts = echarts.init(chartDom)
  504. let charts = {
  505. nodes: this.nodes,
  506. linesData: this.linesData
  507. }
  508. let option = {
  509. xAxis: {
  510. min: 0,
  511. max: 600,
  512. padding: [0, 50, 0, 50],
  513. show: false,
  514. type: 'value'
  515. },
  516. yAxis: {
  517. min: 0,
  518. max: 450,
  519. show: false,
  520. type: 'value'
  521. },
  522. grid: {
  523. left: 50,
  524. right: 0,
  525. bottom: 0,
  526. top: 0
  527. },
  528. series: [
  529. {
  530. type: 'graph',
  531. coordinateSystem: 'cartesian2d',
  532. symbol: 'rect',
  533. symbolSize: [80, 40],
  534. itemStyle: {
  535. color: 'rgb(225,7,7)'
  536. },
  537. symbolOffset: [10, 0],
  538. // force: {
  539. // edgeLength: 100,//连线的长度
  540. // repulsion: 200 //子节点之间的间距
  541. // },
  542. label: {
  543. show: true,
  544. color: 'white' // 节点文字颜色
  545. },
  546. data: charts.nodes
  547. },
  548. {
  549. type: 'lines',
  550. polyline: false,
  551. coordinateSystem: 'cartesian2d',
  552. symbol: ['', 'arrow'],
  553. symbolSize: 10,
  554. data: charts.linesData
  555. }
  556. ]
  557. }
  558. myCharts.clear()
  559. myCharts.setOption(option)
  560. window.addEventListener('resize', () => {
  561. myCharts.resize()
  562. })
  563. })
  564. },
  565. changeAddWorkTime(totalAddTime) {
  566. this.formData.totalAddTime = totalAddTime
  567. },
  568. getHour(s1, s2) {
  569. var reDate = /\d{4}-\d{1,2}-\d{1,2} /
  570. s1 = new Date((reDate.test(s1) ? s1 : '2018-1-1 ' + s1).replace(/-/g, '/'))
  571. s2 = new Date((reDate.test(s2) ? s2 : '2018-1-1 ' + s2).replace(/-/g, '/'))
  572. var ms = s2.getTime() - s1.getTime()
  573. if (ms < 0) return 0
  574. console.log(ms)
  575. return (ms / 1000 / 60 / 60).toFixed(1) //小时
  576. },
  577. getTimeRange(event, i) {
  578. this.$nextTick(() => {
  579. console.log(event)
  580. if (event) {
  581. this.applyAddWorkTime[i].startTime = this.applyAddWorkTime[i].startDay + ' ' + event[0] + ':00'
  582. this.applyAddWorkTime[i].endTime = this.applyAddWorkTime[i].startDay + ' ' + event[1] + ':00'
  583. let timeRang = this.getHour(event[0], event[1])
  584. this.applyAddWorkTime[i].useTime = timeRang
  585. } else {
  586. this.applyAddWorkTime[i].useTime = 0
  587. }
  588. })
  589. },
  590. async getUserInfo() {
  591. let { data: userinfo } = await this.baseRequest1('ApplyAddWorkController', 'getUserInfoByUserId', { userId: '' })
  592. this.userinfo = userinfo
  593. console.log(this.userinfo)
  594. },
  595. baseRequest1(prefix, opUrl, postData) {
  596. return this.$channel.globleRequest(prefix, opUrl, postData, 'project task')
  597. },
  598. addListRow() {
  599. this.applyAddWorkTime.push({ ...applyAddWorkTime })
  600. },
  601. spliceListRow(index) {
  602. this.applyAddWorkTime.splice(index, 1)
  603. },
  604. setVisible(status) {
  605. this.getUserInfo()
  606. this.dialogVisible = status
  607. },
  608. onOpen() {
  609. },
  610. onClose() {
  611. this.$refs['elForm'].resetFields()
  612. },
  613. close() {
  614. this.$emit('update:visible', false)
  615. },
  616. async handelConfirm() {
  617. this.$refs['elForm'].validate(async valid => {
  618. if (!valid) return
  619. let formData = {
  620. ...this.formData,
  621. applyAddWorkTimeString: JSON.stringify(this.applyAddWorkTime)
  622. }
  623. delete formData.applyAddWorkTime
  624. this.loading = true
  625. let { data } = await this.baseRequest1('ApplyAddWorkController', 'addApplyAddWork', { ...formData })
  626. if (data.code == 200) {
  627. this.loading = false
  628. this.$message.success('加班申请发起成功')
  629. this.dialogVisible = false
  630. this.$emit('getData')
  631. }
  632. this.close()
  633. })
  634. }
  635. }
  636. }
  637. </script>
  638. <style lang="scss">
  639. .cclist {
  640. .col-input {
  641. padding: 0;
  642. }
  643. }
  644. #containeraddwork {
  645. height: 600px;
  646. width: 100%;
  647. background: #F5F5F5;
  648. }
  649. .pdr10px {
  650. padding-right: 10px;
  651. }
  652. .mgb10px {
  653. margin-bottom: 10px;
  654. }
  655. .mb25 {
  656. margin-bottom: 25px;
  657. }
  658. .pdtopbottom16 {
  659. padding: 0px 16px;
  660. }
  661. .pdtop16px {
  662. padding-top: 16px;
  663. }
  664. .totalApplyTime {
  665. font-size: 16px;
  666. font-family: 微软雅黑;
  667. font-weight: 400;
  668. color: #1890FF;
  669. text-align: right;
  670. margin: 15px 0 15px 0;
  671. width: 100%;
  672. }
  673. .cost_form {
  674. .col-input {
  675. font-weight: 400;
  676. }
  677. .el-form-item__label .moneydetails {
  678. text-align: right;
  679. font-size: 16px;
  680. font-family: 微软雅黑;
  681. padding-right: 10px;
  682. line-height: 40px;
  683. word-break: keep-all;
  684. white-space: nowrap;
  685. color: #606266;
  686. text-rendering: optimizeLegibility;
  687. font-weight: 400;
  688. }
  689. .moneydetails {
  690. text-align: right;
  691. font-size: 16px;
  692. font-family: 微软雅黑;
  693. padding-right: 10px;
  694. word-break: keep-all;
  695. white-space: nowrap;
  696. color: #606266;
  697. text-rendering: optimizeLegibility;
  698. font-weight: 400;
  699. }
  700. .moneydetails:before {
  701. content: "*";
  702. color: #ff4949;
  703. }
  704. }
  705. .txtc {
  706. text-align: center
  707. }
  708. .ml5 {
  709. margin-left: 5px;
  710. }
  711. .eltype {
  712. margin-bottom: 15px;
  713. }
  714. .tabsdom {
  715. .el-input {
  716. width: 100%;
  717. }
  718. .el-tabs__header {
  719. text-align: center !important;
  720. width: 139px !important;
  721. text-align: center !important;
  722. display: block !important;
  723. margin: auto !important;
  724. margin-bottom: 15px !important;
  725. }
  726. .el-tabs__nav-wrap::after {
  727. display: none;
  728. }
  729. .el-upload {
  730. width: 100%;
  731. }
  732. }
  733. .feeMoneyTotal {
  734. width: 100%;
  735. height: 14px;
  736. font-size: 14px;
  737. font-weight: 400;
  738. color: #1890FF;
  739. margin-top: 31px;
  740. margin-bottom: 13px;
  741. }
  742. </style>