index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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. <!-- <AddModal
  90. ref="addModal"
  91. @submit="onSubmit"
  92. :projectList="projectList"
  93. :typeList="typeList"
  94. /> -->
  95. <view class="group">
  96. <button @click="onHandleAudit({}, 2)" type="primary">上报本月工时</button>
  97. <button
  98. v-show="collapseList.length > 0"
  99. @click="onHandleAudit({}, 1)"
  100. class="button"
  101. >
  102. 上报今日工时
  103. </button>
  104. </view>
  105. </view>
  106. </template>
  107. <script>
  108. import {
  109. queryAllWorkType,
  110. queryWorkHours,
  111. addWorkHours,
  112. addAuthWorkHours,
  113. queryProject,
  114. } from "@/services/workload";
  115. import AddModal from "./AddModal.vue";
  116. import moment from "moment";
  117. import { mapState } from "vuex";
  118. export default {
  119. components: { AddModal },
  120. data() {
  121. return {
  122. info: {
  123. lunar: true,
  124. range: true,
  125. insert: false,
  126. selected: [],
  127. },
  128. currentItem: {},
  129. projectList: [],
  130. currentList: [],
  131. stateColor: {
  132. 1: "#1890ff",
  133. 2: "#a0d911",
  134. 3: "#f5222d",
  135. },
  136. auditState: {
  137. 0: "未提审",
  138. 1: "待审核",
  139. 2: "已通过",
  140. 3: "已拒绝",
  141. },
  142. auditType: 0,
  143. day: moment().format("YYYY-MM-DD"),
  144. monthDate: moment(),
  145. };
  146. },
  147. onNavigationBarButtonTap(e) {
  148. uni.navigateTo({
  149. url: "../audit/index",
  150. });
  151. },
  152. onLoad() {
  153. this.init();
  154. },
  155. onShow() {
  156. this.queryWorkHours();
  157. },
  158. computed: {
  159. ...mapState(["allType", "typeList"]),
  160. collapseList() {
  161. const allType = this.allType;
  162. let data = {};
  163. this.currentList.forEach((item) => {
  164. let pid = allType[item.type_id]?.parent_id;
  165. if (!data[pid]) data[pid] = [];
  166. data[pid].push(item);
  167. });
  168. return Object.keys(data).map((pid) => ({
  169. name: allType[pid]?.name,
  170. children: data[pid],
  171. }));
  172. },
  173. },
  174. methods: {
  175. async init() {
  176. await this.$store.dispatch("getType");
  177. this.projectList = await queryProject();
  178. this.queryWorkHours();
  179. },
  180. change(e) {
  181. console.log(e);
  182. this.day = e.fulldate;
  183. this.currentList = e.extraInfo.list || [];
  184. },
  185. monthSwitch(e) {
  186. this.monthDate = moment(`${e.year}-${e.month}-01`, "YYYY-MM-DD");
  187. this.day = `${e.year}-${e.month < 10 ? "0" + e.month : e.month}-${moment(this.day).format("DD")}`;
  188. this.currentList = [];
  189. this.queryWorkHours();
  190. // this.day = e.fulldate;
  191. // this.currentList = e.extraInfo.list || [];
  192. },
  193. getProject(id) {
  194. let p = this.projectList.find((p) => p.ID == id);
  195. if (!p) return "";
  196. return p.Name;
  197. },
  198. onHandleSave(item) {
  199. this.currentItem = item;
  200. this.$refs.inputDialog.open();
  201. },
  202. onHandleAudit(item, type) {
  203. this.currentItem = item;
  204. this.auditType = type;
  205. this.$refs.auditDialog.open();
  206. },
  207. async dialogInputConfirm(workload) {
  208. let item = this.currentItem;
  209. if (isNaN(workload)) {
  210. uni.showToast({
  211. title: "工时必须输入数字!",
  212. icon: "none",
  213. });
  214. return;
  215. }
  216. let decimal = workload.split(".")[1];
  217. if (decimal && decimal != 5) {
  218. uni.showToast({
  219. title: "工时最小精确到半小时!",
  220. icon: "none",
  221. });
  222. return;
  223. }
  224. let currentWorkload = this.currentList.reduce((total, temp) => {
  225. if (temp.id == item.id) return total;
  226. return total + temp.workload;
  227. }, 0);
  228. if (currentWorkload + Number(workload) > 8) {
  229. uni.showToast({
  230. title: "每日工时不能超过8小时!",
  231. icon: "none",
  232. });
  233. return;
  234. }
  235. let params = [
  236. {
  237. type_id: Number(item.type_id),
  238. comment: "",
  239. data: [
  240. {
  241. id: item.id,
  242. project_id: Number(item.project_id),
  243. workload: Number(workload),
  244. day: item.day,
  245. },
  246. ],
  247. },
  248. ];
  249. // 保存
  250. await addWorkHours(params);
  251. await this.queryWorkHours();
  252. },
  253. async auditConfirm() {
  254. let type = this.auditType;
  255. let params;
  256. const getData = (item) => {
  257. return {
  258. type_id: Number(item.type_id),
  259. data: [
  260. {
  261. id: item.id,
  262. project_id: item.project_id,
  263. workload: item.workload,
  264. day: item.day,
  265. },
  266. ],
  267. };
  268. };
  269. if (type == 0) {
  270. params = [getData(this.currentItem)];
  271. } else if (type == 1) {
  272. let auditList = this.currentList.filter(
  273. (item) => item.audit_state == 0 || item.audit_state == 3
  274. );
  275. if (auditList.length == 0) {
  276. uni.showToast({
  277. title: "本日没有待上报工时!",
  278. icon: "none",
  279. });
  280. return;
  281. }
  282. params = auditList.map((item) => getData(item));
  283. } else {
  284. let auditList = [];
  285. this.info.selected.forEach((s) => {
  286. if (s.list) {
  287. auditList = auditList.concat(
  288. s.list.filter(
  289. (item) => item.audit_state == 0 || item.audit_state == 3
  290. )
  291. );
  292. }
  293. });
  294. if (auditList.length == 0) {
  295. uni.showToast({
  296. title: "本月没有待上报工时!",
  297. icon: "none",
  298. });
  299. return;
  300. }
  301. params = auditList.map((item) => getData(item));
  302. }
  303. await addAuthWorkHours(params);
  304. this.queryWorkHours();
  305. this.$refs.auditDialog.close();
  306. },
  307. async queryWorkHours() {
  308. let date = this.monthDate;
  309. let s_time = date.format("YYYY-MM-01 00:00:00");
  310. let e_time = moment(s_time)
  311. .add("month", 1)
  312. .add("days", -1)
  313. .format("YYYY-MM-DD 23:59:59");
  314. const user = uni.getStorageSync("user");
  315. const data = await queryWorkHours({ user_id: user.ID, s_time, e_time });
  316. let selected = [];
  317. Object.keys(data).map((day) => {
  318. let list = data[day];
  319. let total = 0;
  320. let canEdit = false;
  321. list.forEach((item) => {
  322. total += item.workload;
  323. if (item.audit_state == 3 || item.audit_state == 0) {
  324. canEdit = true;
  325. }
  326. });
  327. selected.push({
  328. date: day,
  329. info: `${total} 小时`,
  330. list: list,
  331. color: canEdit ? "#e43d33" : "",
  332. });
  333. if (this.day == day) {
  334. this.currentList = list;
  335. }
  336. });
  337. this.info.selected = selected;
  338. },
  339. toAdd() {
  340. uni.navigateTo({
  341. url: `./add?day=${this.day}`,
  342. });
  343. },
  344. // async onSubmit(values) {
  345. // let params = [
  346. // {
  347. // type_id: Number(values.subTypeId),
  348. // comment: "",
  349. // data: [
  350. // {
  351. // project_id: Number(values.projectId),
  352. // workload: 0,
  353. // day: this.day,
  354. // },
  355. // ],
  356. // },
  357. // ];
  358. // // 新增
  359. // await addWorkHours(params);
  360. // this.queryWorkHours();
  361. // },
  362. // showModal() {
  363. // this.$refs.addModal.open();
  364. // },
  365. },
  366. };
  367. </script>
  368. <style lang="scss" scoped>
  369. .content {
  370. padding: 30rpx;
  371. padding-top: 0;
  372. padding-bottom: 60px;
  373. }
  374. // .button {
  375. // margin-right: 20rpx;
  376. // }
  377. .card-actions {
  378. display: flex;
  379. flex-direction: row;
  380. justify-content: space-around;
  381. align-items: center;
  382. height: 45px;
  383. border-top: 1px #eee solid;
  384. }
  385. .detail-item {
  386. margin: 12rpx 0;
  387. display: flex;
  388. justify-content: space-between;
  389. align-items: flex-start;
  390. .detail-value {
  391. max-width: 60%;
  392. text-align: right;
  393. }
  394. }
  395. .group {
  396. width: 100%;
  397. display: flex;
  398. position: fixed;
  399. justify-content: flex-start;
  400. bottom: 0;
  401. left: 0;
  402. button {
  403. width: 50%;
  404. border-radius: 0;
  405. margin: inherit;
  406. }
  407. }
  408. </style>