index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. <template>
  2. <view class="content">
  3. <!-- <button class="btn-add" @click="toAdd" type="primary">新增</button> -->
  4. <uni-calendar
  5. class="uni-calendar--hook"
  6. :selected="info.selected"
  7. :showMonth="false"
  8. :date="day"
  9. @change="change"
  10. @monthSwitch="monthSwitch"
  11. >
  12. <text @click="toAdd">新增工时</text>
  13. </uni-calendar>
  14. <!-- <button @click="showModal" type="primary">新增</button> -->
  15. <uni-collapse ref="collapse">
  16. <uni-collapse-item
  17. v-for="collapse in collapseList"
  18. :key="collapse.name"
  19. :title="collapse.name"
  20. >
  21. <view class="content">
  22. <uni-card
  23. v-for="(item, index) in collapse.children"
  24. :key="index"
  25. :title="allType[item.type_id] && allType[item.type_id].name"
  26. >
  27. <view class="uni-body">
  28. <view class="detail-item" v-if="item.project_id != '0'">
  29. <view class="detail-label">所属项目</view>
  30. <view class="detail-value">
  31. {{ getProject(item.project_id) }}
  32. </view>
  33. </view>
  34. <view class="detail-item">
  35. <view class="detail-label">审核状态</view>
  36. <view
  37. class="detail-value"
  38. :style="{ color: stateColor[item.audit_state] }"
  39. >
  40. {{ auditState[item.audit_state] }}
  41. </view>
  42. </view>
  43. <view class="detail-item" v-if="item.audit_state == 3">
  44. <view class="detail-label">拒绝原因</view>
  45. <view class="detail-value">
  46. {{ item.audit_desc }}
  47. </view>
  48. </view>
  49. <view class="detail-item">
  50. <view class="detail-label">工时</view>
  51. <view class="detail-value">
  52. {{ item.workload }}
  53. </view>
  54. </view>
  55. </view>
  56. <view
  57. v-if="item.audit_state == 0 || item.audit_state == 3"
  58. slot="actions"
  59. class="card-actions"
  60. >
  61. <view class="card-actions-item" @click="onHandleSave(item)">
  62. <text class="card-actions-item-text">修改工时</text>
  63. </view>
  64. <view class="card-actions-item" @click="onHandleAudit(item, 0)">
  65. <text class="card-actions-item-text">上报审批</text>
  66. </view>
  67. </view>
  68. </uni-card>
  69. </view>
  70. </uni-collapse-item>
  71. </uni-collapse>
  72. <uni-popup ref="inputDialog" type="dialog">
  73. <uni-popup-dialog
  74. ref="inputClose"
  75. mode="input"
  76. title="填入工时"
  77. :value="currentItem.workload === 0 ? '' : currentItem.workload"
  78. @confirm="dialogInputConfirm"
  79. ></uni-popup-dialog>
  80. </uni-popup>
  81. <uni-popup ref="auditDialog" type="dialog">
  82. <uni-popup-dialog
  83. type="info"
  84. title="提示"
  85. content="是否确认提交审批"
  86. @confirm="auditConfirm"
  87. ></uni-popup-dialog>
  88. </uni-popup>
  89. <view class="group">
  90. <!-- <button
  91. class="approval"
  92. v-if="permission['func-05-mobile-works-approval']"
  93. @click="onHandleApproval()"
  94. >
  95. 审批工时
  96. </button> -->
  97. <button
  98. v-if="permission['func-05-mobile-works-commit']"
  99. @click="onHandleAudit({}, 2)"
  100. type="primary"
  101. class="commit"
  102. >
  103. 上报本月工时
  104. </button>
  105. <button
  106. v-if="permission['func-05-mobile-works-commit']"
  107. v-show="collapseList.length > 0"
  108. @click="onHandleAudit({}, 1)"
  109. class="commit"
  110. >
  111. 上报今日工时
  112. </button>
  113. </view>
  114. </view>
  115. </template>
  116. <script>
  117. import {
  118. queryAllWorkType,
  119. queryWorkHours,
  120. addWorkHours,
  121. addAuthWorkHours,
  122. queryProject,
  123. } from "@/services/workload";
  124. import moment from "moment";
  125. import { mapState } from "vuex";
  126. export default {
  127. data() {
  128. return {
  129. info: {
  130. lunar: true,
  131. range: true,
  132. insert: false,
  133. selected: [],
  134. },
  135. currentItem: {},
  136. projectList: [],
  137. currentList: [],
  138. stateColor: {
  139. 1: "#1890ff",
  140. 2: "#a0d911",
  141. 3: "#f5222d",
  142. },
  143. auditState: {
  144. 0: "未提审",
  145. 1: "待审核",
  146. 2: "已通过",
  147. 3: "已拒绝",
  148. },
  149. auditType: 0,
  150. day: moment().format("YYYY-MM-DD"),
  151. monthDate: moment(),
  152. permission: {},
  153. };
  154. },
  155. onLoad() {
  156. this.init();
  157. },
  158. onShow() {
  159. this.queryWorkHours();
  160. },
  161. computed: {
  162. ...mapState(["allType"]),
  163. collapseList() {
  164. const allType = this.allType;
  165. let data = {};
  166. this.currentList.forEach((item) => {
  167. let pid = allType[item.type_id]?.parent_id;
  168. if (!data[pid]) data[pid] = [];
  169. data[pid].push(item);
  170. });
  171. return Object.keys(data).map((pid) => ({
  172. name: allType[pid]?.name,
  173. children: data[pid],
  174. }));
  175. },
  176. },
  177. methods: {
  178. async init() {
  179. await this.$store.dispatch("getType");
  180. this.projectList = await queryProject();
  181. this.queryWorkHours();
  182. },
  183. change(e) {
  184. this.day = e.fulldate;
  185. this.currentList = e.extraInfo.list || [];
  186. },
  187. monthSwitch(e) {
  188. this.monthDate = moment(`${e.year}-${e.month}-01`, "YYYY-MM-DD");
  189. this.day = `${e.year}-${e.month < 10 ? "0" + e.month : e.month}-${moment(
  190. this.day
  191. ).format("DD")}`;
  192. this.currentList = [];
  193. this.queryWorkHours();
  194. // this.day = e.fulldate;
  195. // this.currentList = e.extraInfo.list || [];
  196. },
  197. getProject(id) {
  198. let p = this.projectList.find((p) => p.ID == id);
  199. if (!p) return "";
  200. return p.Name;
  201. },
  202. onHandleSave(item) {
  203. this.currentItem = item;
  204. this.$refs.inputDialog.open();
  205. },
  206. onHandleAudit(item, type) {
  207. this.currentItem = item;
  208. this.auditType = type;
  209. this.$refs.auditDialog.open();
  210. },
  211. onHandleApproval() {
  212. uni.navigateTo({
  213. url: "../WorkingHours/audit",
  214. });
  215. },
  216. async dialogInputConfirm(workload) {
  217. let item = this.currentItem;
  218. if (isNaN(workload)) {
  219. uni.showToast({
  220. title: "工时必须输入数字!",
  221. icon: "none",
  222. });
  223. return;
  224. }
  225. let decimal = workload.split(".")[1];
  226. if (decimal && decimal != 5) {
  227. uni.showToast({
  228. title: "工时最小精确到半小时!",
  229. icon: "none",
  230. });
  231. return;
  232. }
  233. let currentWorkload = this.currentList.reduce((total, temp) => {
  234. if (temp.id == item.id) return total;
  235. return total + temp.workload;
  236. }, 0);
  237. if (currentWorkload + Number(workload) > 8) {
  238. uni.showToast({
  239. title: "每日工时不能超过8小时!",
  240. icon: "none",
  241. });
  242. return;
  243. }
  244. let params = [
  245. {
  246. type_id: Number(item.type_id),
  247. comment: "",
  248. data: [
  249. {
  250. id: item.id,
  251. project_id: Number(item.project_id),
  252. workload: Number(workload),
  253. day: item.day,
  254. },
  255. ],
  256. },
  257. ];
  258. // 保存
  259. await addWorkHours(params);
  260. await this.queryWorkHours();
  261. },
  262. async auditConfirm() {
  263. let type = this.auditType;
  264. let params;
  265. const getData = (item) => {
  266. return {
  267. type_id: Number(item.type_id),
  268. data: [
  269. {
  270. id: item.id,
  271. project_id: item.project_id,
  272. workload: item.workload,
  273. day: item.day,
  274. },
  275. ],
  276. };
  277. };
  278. if (type == 0) {
  279. params = [getData(this.currentItem)];
  280. } else if (type == 1) {
  281. let auditList = this.currentList.filter(
  282. (item) => item.audit_state == 0 || item.audit_state == 3
  283. );
  284. if (auditList.length == 0) {
  285. uni.showToast({
  286. title: "本日没有待上报工时!",
  287. icon: "none",
  288. });
  289. return;
  290. }
  291. params = auditList.map((item) => getData(item));
  292. } else {
  293. let auditList = [];
  294. this.info.selected.forEach((s) => {
  295. if (s.list) {
  296. auditList = auditList.concat(
  297. s.list.filter(
  298. (item) => item.audit_state == 0 || item.audit_state == 3
  299. )
  300. );
  301. }
  302. });
  303. if (auditList.length == 0) {
  304. uni.showToast({
  305. title: "本月没有待上报工时!",
  306. icon: "none",
  307. });
  308. return;
  309. }
  310. params = auditList.map((item) => getData(item));
  311. }
  312. await addAuthWorkHours(params);
  313. this.queryWorkHours();
  314. this.$refs.auditDialog.close();
  315. },
  316. async queryWorkHours() {
  317. let date = this.monthDate;
  318. let s_time = date.format("YYYY-MM-01 00:00:00");
  319. let e_time = moment(s_time)
  320. .add("month", 1)
  321. .add("days", -1)
  322. .format("YYYY-MM-DD 23:59:59");
  323. const user = uni.getStorageSync("user");
  324. const data = await queryWorkHours({ user_id: user.ID, s_time, e_time });
  325. let selected = [];
  326. Object.keys(data).map((day) => {
  327. let list = data[day];
  328. let total = 0;
  329. let canEdit = false;
  330. list.forEach((item) => {
  331. total += item.workload;
  332. if (item.audit_state == 3 || item.audit_state == 0) {
  333. canEdit = true;
  334. }
  335. });
  336. selected.push({
  337. date: day,
  338. info: `${total} 小时`,
  339. list: list,
  340. color: canEdit ? "#e43d33" : "",
  341. });
  342. if (this.day == day) {
  343. this.currentList = list;
  344. }
  345. });
  346. this.info.selected = selected;
  347. let permission = {};
  348. user.Permissions?.forEach((item) => {
  349. permission = {
  350. ...permission,
  351. ...item.Menus,
  352. };
  353. });
  354. this.permission = permission;
  355. },
  356. toAdd() {
  357. uni.navigateTo({
  358. url: `./add?day=${this.day}`,
  359. });
  360. },
  361. // async onSubmit(values) {
  362. // let params = [
  363. // {
  364. // type_id: Number(values.subTypeId),
  365. // comment: "",
  366. // data: [
  367. // {
  368. // project_id: Number(values.projectId),
  369. // workload: 0,
  370. // day: this.day,
  371. // },
  372. // ],
  373. // },
  374. // ];
  375. // // 新增
  376. // await addWorkHours(params);
  377. // this.queryWorkHours();
  378. // },
  379. // showModal() {
  380. // this.$refs.addModal.open();
  381. // },
  382. },
  383. };
  384. </script>
  385. <style lang="scss" scoped>
  386. .content {
  387. padding: 30rpx;
  388. padding-top: 0;
  389. padding-bottom: 60px;
  390. }
  391. // .button {
  392. // margin-right: 20rpx;
  393. // }
  394. .card-actions {
  395. display: flex;
  396. flex-direction: row;
  397. justify-content: space-around;
  398. align-items: center;
  399. height: 45px;
  400. border-top: 1px #eee solid;
  401. }
  402. .detail-item {
  403. margin: 12rpx 0;
  404. display: flex;
  405. justify-content: space-between;
  406. align-items: flex-start;
  407. .detail-value {
  408. max-width: 60%;
  409. text-align: right;
  410. }
  411. }
  412. .group {
  413. width: 100%;
  414. display: flex;
  415. position: fixed;
  416. flex-wrap: wrap;
  417. justify-content: flex-start;
  418. bottom: 0;
  419. left: 0;
  420. .approval {
  421. width: 100%;
  422. border-radius: 0;
  423. margin: inherit;
  424. }
  425. .commit {
  426. width: 50%;
  427. border-radius: 0;
  428. margin: inherit;
  429. }
  430. }
  431. </style>