index.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <template>
  2. <view class="content">
  3. <uni-calendar
  4. class="uni-calendar--hook"
  5. :selected="info.selected"
  6. :showMonth="false"
  7. @change="change"
  8. />
  9. <button @click="showModal" type="primary">新增</button>
  10. <uni-collapse ref="collapse">
  11. <uni-collapse-item
  12. v-for="collapse in collapseList"
  13. :key="collapse.name"
  14. :title="collapse.name"
  15. >
  16. <view class="content">
  17. <template v-for="(item, index) in collapse.children">
  18. <uni-card
  19. :key="index"
  20. :title="allType[item.type_id] && allType[item.type_id].name"
  21. >
  22. <view class="uni-body">
  23. <view class="detail-item" v-if="item.project_id != '0'">
  24. <view class="detail-label">所属项目</view>
  25. <view class="detail-value">
  26. {{ getProject(item.project_id) }}
  27. </view>
  28. </view>
  29. <view class="detail-item">
  30. <view class="detail-label">审核状态</view>
  31. <view
  32. class="detail-value"
  33. :style="{ color: stateColor[item.audit_state] }"
  34. >
  35. {{ auditState[item.audit_state] }}
  36. </view>
  37. </view>
  38. <view class="detail-item" v-if="item.audit_state == 3">
  39. <view class="detail-label">拒绝原因</view>
  40. <view class="detail-value">
  41. {{ item.audit_desc }}
  42. </view>
  43. </view>
  44. <view class="detail-item">
  45. <view class="detail-label">工时</view>
  46. <view class="detail-value">
  47. {{ item.workload }}
  48. </view>
  49. </view>
  50. </view>
  51. <view
  52. v-if="item.audit_state == 0 || item.audit_state == 3"
  53. slot="actions"
  54. class="card-actions"
  55. >
  56. <view class="card-actions-item" @click="onHandleSave(item)">
  57. <text class="card-actions-item-text">修改工时</text>
  58. </view>
  59. <view class="card-actions-item" @click="onHandleAudit(item)">
  60. <text class="card-actions-item-text">上报审批</text>
  61. </view>
  62. </view>
  63. </uni-card>
  64. </template>
  65. </view>
  66. </uni-collapse-item>
  67. </uni-collapse>
  68. <uni-popup ref="inputDialog" type="dialog">
  69. <uni-popup-dialog
  70. ref="inputClose"
  71. mode="input"
  72. title="填入工时"
  73. :value="currentItem.workload === 0 ? '' : currentItem.workload"
  74. @confirm="dialogInputConfirm"
  75. ></uni-popup-dialog>
  76. </uni-popup>
  77. <uni-popup ref="auditDialog" type="dialog">
  78. <uni-popup-dialog
  79. type="info"
  80. title="提示"
  81. content="是否确认提交审批"
  82. @confirm="auditConfirm"
  83. ></uni-popup-dialog>
  84. </uni-popup>
  85. <AddModal
  86. ref="addModal"
  87. @submit="onSubmit"
  88. :projectList="projectList"
  89. :typeList="typeList"
  90. />
  91. </view>
  92. </template>
  93. <script>
  94. import {
  95. queryAllWorkType,
  96. queryWorkHours,
  97. addWorkHours,
  98. addAuthWorkHours,
  99. queryProject,
  100. } from "@/services/workload";
  101. import AddModal from "./AddModal.vue";
  102. import moment from "moment";
  103. import { mapState } from "vuex";
  104. export default {
  105. components: { AddModal },
  106. data() {
  107. return {
  108. info: {
  109. lunar: true,
  110. range: true,
  111. insert: false,
  112. selected: [],
  113. },
  114. currentItem: {},
  115. projectList: [],
  116. currentList: [],
  117. stateColor: {
  118. 1: "#1890ff",
  119. 2: "#a0d911",
  120. 3: "#f5222d",
  121. },
  122. auditState: {
  123. 0: "未提审",
  124. 1: "待审核",
  125. 2: "已通过",
  126. 3: "已拒绝",
  127. },
  128. day: moment().format("YYYY-MM-DD"),
  129. };
  130. },
  131. onNavigationBarButtonTap(e) {
  132. uni.navigateTo({
  133. url: "../audit/index",
  134. });
  135. },
  136. onReady() {
  137. this.init();
  138. },
  139. computed: {
  140. ...mapState(["allType", "typeList"]),
  141. collapseList() {
  142. const allType = this.allType;
  143. let data = {};
  144. this.currentList.forEach((item) => {
  145. let pid = allType[item.type_id]?.parent_id;
  146. if (!data[pid]) data[pid] = [];
  147. data[pid].push(item);
  148. });
  149. return Object.keys(data).map((pid) => ({
  150. name: allType[pid]?.name,
  151. children: data[pid],
  152. }));
  153. },
  154. },
  155. methods: {
  156. async init() {
  157. await this.$store.dispatch("getType");
  158. this.projectList = await queryProject();
  159. await this.queryWorkHours();
  160. },
  161. change(e) {
  162. this.day = e.fulldate;
  163. this.currentList = e.extraInfo.list || [];
  164. },
  165. getProject(id) {
  166. let p = this.projectList.find((p) => p.ID == id);
  167. if (!p) return "";
  168. return p.Name;
  169. },
  170. onHandleSave(item) {
  171. this.currentItem = item;
  172. this.$refs.inputDialog.open();
  173. },
  174. onHandleAudit(item) {
  175. this.currentItem = item;
  176. this.$refs.auditDialog.open();
  177. },
  178. async dialogInputConfirm(workload) {
  179. let item = this.currentItem;
  180. if (isNaN(workload)) {
  181. uni.showToast({
  182. title: "工时必须输入数字!",
  183. icon: "none",
  184. });
  185. return;
  186. }
  187. let decimal = workload.split(".")[1];
  188. if (decimal && decimal != 5) {
  189. uni.showToast({
  190. title: "工时最小精确到半小时!",
  191. icon: "none",
  192. });
  193. return;
  194. }
  195. let currentWorkload = this.currentList.reduce((total, temp) => {
  196. if (temp.id == item.id) return total;
  197. return total + temp.workload;
  198. }, 0);
  199. if (currentWorkload + Number(workload) > 8) {
  200. uni.showToast({
  201. title: "每日工时不能超过8小时!",
  202. icon: "none",
  203. });
  204. return;
  205. }
  206. let params = [
  207. {
  208. type_id: Number(item.type_id),
  209. comment: "",
  210. data: [
  211. {
  212. id: item.id,
  213. project_id: Number(item.project_id),
  214. workload: Number(workload),
  215. day: item.time,
  216. },
  217. ],
  218. },
  219. ];
  220. // 保存
  221. await addWorkHours(params);
  222. await this.queryWorkHours();
  223. },
  224. async auditConfirm() {
  225. let item = this.currentItem;
  226. let params = [
  227. {
  228. type_id: Number(item.type_id),
  229. data: [
  230. {
  231. id: item.id,
  232. project_id: item.project_id,
  233. workload: item.workload,
  234. day: item.time,
  235. },
  236. ],
  237. },
  238. ];
  239. await addAuthWorkHours(params);
  240. this.queryWorkHours();
  241. this.$refs.auditDialog.close();
  242. },
  243. async queryWorkHours() {
  244. const user = uni.getStorageSync("user");
  245. const data = await queryWorkHours({ user_id: user.ID });
  246. let selected = [];
  247. Object.keys(data).map((day) => {
  248. let list = data[day];
  249. let total = 0;
  250. let canEdit = false;
  251. list.forEach((item) => {
  252. total += item.workload;
  253. if (item.audit_state == 3 || item.audit_state == 0) {
  254. canEdit = true;
  255. }
  256. });
  257. selected.push({
  258. date: day,
  259. info: `${total} 小时`,
  260. list: list,
  261. color: canEdit ? "#e43d33" : "",
  262. });
  263. if (this.day == day) {
  264. this.currentList = list;
  265. }
  266. });
  267. this.info.selected = selected;
  268. },
  269. async onSubmit(values) {
  270. let params = [
  271. {
  272. type_id: Number(values.subTypeId),
  273. comment: "",
  274. data: [
  275. {
  276. project_id: Number(values.projectId),
  277. workload: 0,
  278. day: this.day,
  279. },
  280. ],
  281. },
  282. ];
  283. // 新增
  284. await addWorkHours(params);
  285. this.queryWorkHours();
  286. },
  287. showModal() {
  288. this.$refs.addModal.open();
  289. },
  290. },
  291. };
  292. </script>
  293. <style lang="scss" scoped>
  294. .content {
  295. padding: 30rpx;
  296. padding-top: 0;
  297. }
  298. .button {
  299. margin-right: 20rpx;
  300. }
  301. .card-actions {
  302. display: flex;
  303. flex-direction: row;
  304. justify-content: space-around;
  305. align-items: center;
  306. height: 45px;
  307. border-top: 1px #eee solid;
  308. }
  309. .detail-item {
  310. margin: 12rpx 0;
  311. display: flex;
  312. justify-content: space-between;
  313. align-items: flex-start;
  314. .detail-value {
  315. max-width: 60%;
  316. text-align: right;
  317. }
  318. }
  319. </style>