index.vue 12 KB

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