index.vue 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074
  1. <template>
  2. <div>
  3. <van-nav-bar title="填写日报" left-text="返回" @click-left="back" fixed left-arrow/>
  4. <van-form class="login_form" @submit="register">
  5. <van-field readonly clickable name="datetimePicker" :value="form.createDate" label="时间选择" placeholder="点击选择时间"
  6. @click="showPicker = true" :rules="rules.createDate" />
  7. <van-popup v-model="showPicker" position="bottom">
  8. <van-datetime-picker v-model="currentDate" type="date" :min-date="minDate" :max-date="maxDate" @confirm="changeTime" @cancel="showPicker = false"/>
  9. </van-popup>
  10. <van-cell title="总时长(h)" v-if="reportTimeType.type == 3">
  11. <template>
  12. <van-stepper :disabled="!canEdit" v-model="reportTimeType.allday" @change="changeAllTime" min="0.5" max="12" step="0.5" :decimal-length="1" />
  13. </template>
  14. </van-cell>
  15. <!-- <van-cell title="待分配时长" :value="report.time + 'h'" size="large"></van-cell> -->
  16. <div class="form_domains" v-for="(item,index) in form.domains" :key="item.id">
  17. <div style="float:right;margin-top:10px;margin-right:10px;">
  18. <!-- <van-tag v-if="canEdit&&item.projectName.length>0" color="#fff"
  19. @click="copyProject(index)" style="border: 1px solid #20a0ff;padding:5px;"
  20. icon="plus" type="default" ><span style="color:#666;padding: 0 5px;">复制项目</span></van-tag> -->
  21. <van-tag v-if="index>0&&canEdit" color="#fff"
  22. @click="delPro(index)" style="border: 1px solid #ff0000;padding:5px;margin-left:10px;"
  23. icon="plus" type="default" ><span style="color:#666;padding: 0 5px;">删除项目</span></van-tag>
  24. </div>
  25. <!-- <van-icon v-if="index>0&&canEdit" class="form_del" name="delete" @click="delPro(index)" /> -->
  26. <van-cell-group :title="'项目' + (index+1)">
  27. <van-field readonly name="projectId" clickable :value="item.projectName" label="投入项目" placeholder="请选择投入项目" @click="clickPicker(index)"
  28. :rules="[{ required: true, message: '请选择投入项目' }]"/>
  29. <van-popup v-model="showPickerProject" position="bottom">
  30. <van-picker show-toolbar :columns="project" value-key="projectName" @confirm="choseProject" @cancel="showPickerProject = false" />
  31. </van-popup>
  32. <van-cell title="专业进度" v-if="user.company.packageEngineering == 1">
  33. </van-cell>
  34. <van-field :disabled="!canEdit"
  35. type="number" :name="'progress_'+pItem.professionId" input-align="right"
  36. v-for="pItem in item.professionProgress" :key="pItem.professionId"
  37. :label="'-- '+pItem.professionName"
  38. >
  39. <template slot="input">
  40. <van-stepper v-model="pItem.progress" integer min="0" max="100" />%
  41. </template>
  42. </van-field>
  43. <van-field v-if="user.company.packageProject == 1" readonly name="taskId" :value="item.taskName" label="关联任务" placeholder="请选择关联任务" @click="clickPickerTask(index)"
  44. />
  45. <van-popup v-model="showPickerTask" position="bottom">
  46. <van-picker show-toolbar :columns="item.taskList" value-key="taskName" @confirm="choseTask" @cancel="showPickerTask = false" />
  47. </van-popup>
  48. <!-- <van-field readonly clickable class="form_input" :value="item.workingTime" name="workingTime" label="工作时长" placeholder="请输入工作时长(单位:小时)"
  49. :rules="[{ required: true, message: '请输入工作时长(单位:小时)' }]" @touchstart.native.stop="showNumberKey = true"/>
  50. <van-number-keyboard v-model="item.workingTime" :show="showNumberKey" close-button-text="完成" extra-key="." :maxlength="4" @blur="showNumberKey = false" /> -->
  51. <!-- 常规选择时间的方式 -->
  52. <!-- 全天上下午模式 -->
  53. <div v-if="reportTimeType.multiWorktime==0">
  54. <van-field v-if="reportTimeType.type < 2" readonly clickable :value="reportTimeType.type==0?item.label:(parseFloat(item.workingTime).toFixed(1)+'h')" label="工作时长" placeholder="请选择工作时长(小时)" @click="clickTimePicker(index)"
  55. :rules="[{ required: true, message: '请选择工作时长' }]"/>
  56. <van-popup v-model="showPickerTime" position="bottom">
  57. <van-picker show-toolbar :columns="timeType" value-key="label" @confirm="choseTimePick" @cancel="showPickerTime = false" />
  58. </van-popup>
  59. <!-- 选择数字时间长度模式 -->
  60. <van-popup v-model="showPickerHours" position="bottom">
  61. <van-picker show-toolbar :columns="timeRange"
  62. :default-index="15"
  63. @confirm="choseTimePick" @cancel="showPickerHours = false" />
  64. </van-popup>
  65. <!-- 时间段选择模式 -->
  66. <van-field readonly v-if="reportTimeType.type == 2" clickable name="datetimePicker" :value="item.startTime" label="开始时间" placeholder="点击选择时间"
  67. @click="canEdit?showStartTime = true:''" :disabled="!canEdit" />
  68. <van-popup v-model="showStartTime" position="bottom">
  69. <van-datetime-picker
  70. v-model="startTime"
  71. type="time"
  72. @confirm="confirmTime(item,0);"
  73. @cancel="showStartTime = false"
  74. :min-hour="0"
  75. :max-hour="23"
  76. />
  77. <!-- :filter="filter" 原本这个属性在里面 -->
  78. </van-popup>
  79. <van-field v-if="reportTimeType.type == 2" readonly clickable name="datetimePicker" :value="item.endTime" label="结束时间" placeholder="点击选择时间"
  80. @click="canEdit?showEndTime = true:''" :disabled="!canEdit" />
  81. <van-popup v-model="showEndTime" position="bottom" >
  82. <van-datetime-picker
  83. v-model="endTime"
  84. type="time"
  85. :min-hour="0"
  86. :max-hour="23"
  87. @confirm="confirmTime(item,1)"
  88. @cancel="showEndTime = false"
  89. />
  90. <!-- :filter="filter" 原本这个属性在里面 -->
  91. </van-popup>
  92. <van-cell v-if="reportTimeType.type == 3" >
  93. <template >
  94. <div>
  95. <span>用时占比</span>
  96. <van-slider :disabled="!canEdit" :min="10" :step="10" style="width:120px;display:inline-block;margin-left:50px;" v-model="item.progress" :value="100" @change="item.workingTime = (reportTimeType.allday*item.progress/100).toFixed(1)" >
  97. <template #button>
  98. <div class="custom-button">{{ item.progress }}%</div>
  99. </template>
  100. </van-slider>
  101. <span style="margin-left:10px;float:right;">{{item.workingTime}}小时</span>
  102. </div>
  103. </template>
  104. </van-cell>
  105. <van-field class="form_input" :disabled = "!canEdit"
  106. v-model="item.content" name="content" type="textarea" label="工作事项" placeholder="请输入工作事项"
  107. rows="3" autosize />
  108. </div>
  109. <!-- 多个时间和工作事项的选择方式 -->
  110. <div v-if="reportTimeType.multiWorktime==1">
  111. <div v-for="(timeItem, tindex) in item.worktimeList" :key="tindex" style="position:relative;border:#ccc 0.5px solid;margin:7px;">
  112. <van-tag v-if="tindex>0 && canEdit" style="position:absolute; right:-7px;top:-7px;z-index:10;"
  113. @click="removeTimeItem(item,tindex)">X</van-tag>
  114. <!-- 时间段选择模式 -->
  115. <van-field readonly v-if="reportTimeType.type == 2" :clickable="canEdit" name="datetimePicker"
  116. :value="timeItem.startTime" label="开始时间" placeholder="点击选择时间"
  117. :rules="[{ required: true, message: '必填项' }]"
  118. @click="canEdit?showStartDialog(timeItem):''" />
  119. <van-field v-if="reportTimeType.type == 2" readonly :clickable="canEdit" name="datetimePicker"
  120. :value="timeItem.endTime" label="结束时间" placeholder="点击选择时间"
  121. :rules="[{ required: true, message: '必填项' }]"
  122. @click="canEdit?showEndDialog(timeItem):''" />
  123. <van-field class="form_input" :disabled="!canEdit" style="color:#333;-webkit-text-fill-color:#646566;"
  124. v-model="timeItem.content" name="content" type="textarea" label="工作事项" placeholder="请输入工作事项"
  125. :rules="[{ required: true, message: '必填项' }]"
  126. rows="1" autosize />
  127. </div>
  128. <!--时间选择器 , 做统一处理,不能放到循环里,不然会有多个公用showStartTime,最后一个会现在最上层UI,导致BUG -->
  129. <van-popup v-model="showWorkStartTime" position="bottom">
  130. <van-datetime-picker
  131. v-model="startTime"
  132. type="time"
  133. @confirm="confirmWorkTime(0);"
  134. @cancel="showWorkStartTime = false"
  135. :min-hour="0"
  136. :max-hour="23"
  137. />
  138. <!-- :filter="filter" 原本这个属性在里面 -->
  139. </van-popup>
  140. <van-popup v-model="showWorkEndTime" position="bottom" >
  141. <van-datetime-picker
  142. v-model="endTime"
  143. type="time"
  144. :min-hour="0"
  145. :max-hour="23"
  146. @confirm="confirmWorkTime(1)"
  147. @cancel="showWorkEndTime = false"
  148. />
  149. <!-- :filter="filter" 原本这个属性在里面 -->
  150. </van-popup>
  151. </div>
  152. <div style="width:100%;" v-if="canEdit&&reportTimeType.multiWorktime==1">
  153. <van-tag style="text-align:center;padding:5px;margin-left:15px;border: 1px solid #20a0ff;"
  154. :disabled="!canEdit" @click="addNewWorktime(index, item)"
  155. icon="plus" color="#ffffff" ><span style="color:#999;text-align:center;padding: 0 5px;"> 添加工时 </span></van-tag>
  156. </div>
  157. <div class="overtime" >
  158. <van-checkbox :disabled="!canEdit" v-model="item.isOvertime" v-if="reportTimeType.multiWorktime !=1">加班</van-checkbox>
  159. <van-tag style="position:absolute;right:10px;" v-if="isCorpWX&&canEdit" type="primary" size="large" @click="takePhoto(index)">拍照上传</van-tag>
  160. </div>
  161. <div style="padding:5px;text-align:center;" v-if="!isIOSystem">
  162. <span v-for="(p, index) in item.pics" :key="p" style="margin-right:15px;">
  163. <img :src="p" style="width:100px; height:100px;" @click="showLargeImg(item.pics, index)"/>
  164. </span>
  165. </div>
  166. <div style="padding:5px;text-align:center;" v-if="isIOSystem">
  167. <span v-for="(p, index) in item.iospics" :key="p" style="margin-right:15px;">
  168. <img :src="p" style="width:100px; height:100px;" @click="showLargeImg(item.iospics, index)"/>
  169. </span>
  170. </div>
  171. <van-popup v-model="imgShow" position="bottom" closeable >
  172. <van-swipe class="my-swipe" indicator-color="white">
  173. <van-swipe-item v-for="(picItem, index) in tmpPics" :key="index">
  174. <img :src="picItem" style="width:100%;" />
  175. </van-swipe-item>
  176. </van-swipe>
  177. </van-popup>
  178. </van-cell-group>
  179. </div>
  180. <div style="text-align:center;" >
  181. <van-tag v-if="canEdit" size="large" style="text-align:center;margin:10px;padding:12px;margin-bottom:120px;border: 1px solid #20a0ff;"
  182. :disabled="!canEdit" @click="addNewPro"
  183. icon="plus" color="#ffffff" ><span style="color:#999;text-align:center;padding: 0 50px;"> + 新增项目 </span></van-tag>
  184. </div>
  185. <div class="form_btn" style="position:fixed; bottom:0px;width:100%;">
  186. <van-button v-if="canEdit" block type="info" native-type="submit"> 提交 </van-button>
  187. <p v-if="canEdit&&form.domains.length>0 && form.domains[0].id != null" block type="default"
  188. style="padding-top:30px;font-size:15px;color:#666;margin:0 auto;text-align:center;padding-bottom:10px;background:#ffffff;" @click="deleteReport"> 删除 </p>
  189. </div>
  190. </van-form>
  191. <div style="position:fixed; bottom:0px;width:100%;">
  192. <van-button v-if="canCancel" block type="default" @click="cancel"> 撤销 </van-button>
  193. </div>
  194. <div class="form_tip" v-if="!canEdit && !canCancel"> 已审核无法修改 </div>
  195. </div>
  196. </template>
  197. <script src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
  198. <script>
  199. import wx from 'weixin-js-sdk'
  200. // Vue.prototype.$wx = wx
  201. export default {
  202. data() {
  203. return {
  204. showWorkStartTime:false,
  205. showWorkEndTime:false,
  206. curWorktime:null,
  207. isIOSystem:false,
  208. imgShow: false,
  209. isCorpWX:false,
  210. isWX: false,
  211. showPickerTask:false,
  212. canCancel:false,
  213. canEdit:false,
  214. showEndTime: false,
  215. showStartTime: false,
  216. startTime:'09:00',
  217. endTime: '18:00',
  218. nowTime:new Date(),
  219. showPickerHours: false,
  220. timeRange:[0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0,4.5,5.0,5.5,6.0,6.5,7.0,7.5,8.0,8.5,9.0,9.5,10.0,10.5,11.0,11.5,12.5,13.0,13.5,14.0,14.5,15.0],
  221. selectTime:null,
  222. reportTimeType:{},
  223. user: JSON.parse(localStorage.userInfo),
  224. minDate: new Date(2010, 0, 1),
  225. maxDate: new Date(new Date().getFullYear(),new Date().getMonth(),new Date().getDate()+7),
  226. currentDate: new Date(),
  227. showPickerTime: false,
  228. showPicker: false,
  229. showPickerProject: false,
  230. clickIndex: 0,
  231. clickTimeIndex: 0,
  232. showNumberKey: false,
  233. canClick: 2,
  234. timeType:[],
  235. form: {
  236. createDate: this.format(new Date(new Date()),"yyyy-MM-dd"),
  237. domains: [{
  238. id: null,
  239. projectId: "",
  240. projectName: "",
  241. workingTime: "",
  242. content: "",
  243. state: 2,
  244. multiWorktime:0,
  245. worktimeList:{}
  246. // pics:["https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",
  247. // "https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",
  248. // "https://worktime.ttkuaiban.com/upload/bc4df504fa724e6cab69872e2c1cfb35.png",]
  249. }],
  250. },
  251. rules: {
  252. createDate: [{ required: true, message: '请选择填报日期' }],
  253. },
  254. project: [],
  255. report: "",
  256. loading: false,
  257. finished: false,
  258. // isOvertime: false
  259. tmpPics:[],
  260. };
  261. },
  262. created() {
  263. },
  264. methods: {
  265. showEndDialog(timeItem) {
  266. this.curWorktime = timeItem;
  267. this.showWorkEndTime = true;
  268. },
  269. showStartDialog(timeItem) {
  270. this.curWorktime = timeItem;
  271. this.showWorkStartTime = true;
  272. },
  273. removeTimeItem(item, index) {
  274. item.worktimeList.splice(index, 1);
  275. },
  276. isIOS(){
  277. var u = navigator.userAgent;
  278. var isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
  279. return isiOS;
  280. },
  281. showLargeImg(item, index) {
  282. this.imgShow = true;
  283. this.tmpPics = item;
  284. },
  285. //拍照上传
  286. takePhoto(index) {
  287. var that = this;
  288. wx.chooseImage({
  289. count: 3, // 默认9
  290. sizeType: ['compressed'], // 可以指定是原图还是压缩图,默认二者都有
  291. sourceType: ['camera'], // 可以指定来源是相册还是相机,默认二者都有
  292. defaultCameraMode: "batch", //表示进入拍照界面的默认模式,目前有normal与batch两种选择,normal表示普通单拍模式,batch表示连拍模式,不传该参数则为normal模式。从3.0.26版本开始支持front和batch_front两种值,其中front表示默认为前置摄像头单拍模式,batch_front表示默认为前置摄像头连拍模式。(注:用户进入拍照界面仍然可自由切换两种模式)
  293. isSaveToAlbum: 0, //整型值,0表示拍照时不保存到系统相册,1表示自动保存,默认值是1
  294. success: function (res) {
  295. var localIds = res.localIds; // 返回选定照片的本地ID列表,
  296. // andriod中localId可以作为img标签的src属性显示图片;
  297. // iOS应当使用 getLocalImgData 获取图片base64数据,从而用于img标签的显示(在img标签内使用 wx.chooseImage 的 localid 显示可能会不成功)
  298. if (that.isIOSystem) {
  299. var retArray = [];
  300. for (var i=0;i<localIds.length; i++) {
  301. wx.getLocalImgData({
  302. localId: localIds[i], // 图片的localID
  303. success: function (res) {
  304. var localData = res.localData; // localData是图片的base64数据,可以用img标签显示
  305. retArray.push(localData);
  306. }
  307. });
  308. }
  309. that.form.domains[index].iospics = retArray;
  310. that.form.domains[index].pics = localIds;
  311. } else {
  312. that.form.domains[index].pics = localIds;
  313. that.$forceUpdate();
  314. }
  315. var serverIdList = [];
  316. //立即就上传到企业微信服务器
  317. for (var i=0;i<localIds.length; i++) {
  318. wx.uploadImage({
  319. localId: localIds[i], // 需要上传的图片的本地ID,由chooseImage接口获得
  320. isShowProgressTips: 1, // 默认为1,显示进度提示
  321. success: function (res) {
  322. var serverId = res.serverId; // 返回图片的服务器端ID
  323. serverIdList.push(serverId);
  324. }
  325. });
  326. }
  327. that.form.domains[index].serverPics = serverIdList;
  328. }
  329. });
  330. },
  331. copyProject(index) {
  332. var leftProgress = 10;
  333. if (this.reportTimeType.type == 3) {
  334. //计算已经待分配工时比例
  335. let array = this.form.domains;
  336. let totalProgress = 0;
  337. for (var i=0;i<array.length; i++) {
  338. totalProgress += array[i].progress;
  339. }
  340. if (totalProgress < 100) {
  341. leftProgress = 100 - totalProgress;
  342. }
  343. }
  344. var newIndex = index+1;
  345. var pName = "";
  346. if (this.form.domains[index].projectId != '') {
  347. pName = this.project.filter(p=>p.id == this.form.domains[index].projectId)[0].projectName;
  348. }
  349. var itemDomain = {
  350. id: null,
  351. projectId: this.form.domains[index].projectId,
  352. projectName: pName,
  353. workingTime: this.reportTimeType.type==3?(leftProgress*this.reportTimeType.allday/100).toFixed(1):"",
  354. progress:leftProgress,
  355. content: "",
  356. state: 2,
  357. isOvertime:false,
  358. };
  359. this.form.domains.splice(newIndex, 0,itemDomain);
  360. },
  361. //删除日报
  362. deleteReport() {
  363. this.$dialog.confirm({
  364. title: '删除日报',
  365. message: '确定要删除当天日报吗?'
  366. }).then(() => {
  367. const toast = this.$toast.loading({
  368. forbidClick: true,
  369. duration: 0
  370. });
  371. this.$axios.post("/report/delete", {userId: this.user.id, date:this.form.createDate})
  372. .then(res => {
  373. if(res.code == "ok") {
  374. toast.clear();
  375. this.$toast.success('删除成功');
  376. window.location.reload();
  377. } else {
  378. toast.clear();
  379. this.$toast.fail('删除失败');
  380. }
  381. }).catch(err=> {toast.clear();});
  382. }).catch(() => {});
  383. },
  384. changeAllTime() {
  385. //总时长发生改变,自动按比例计算
  386. this.form.domains.forEach(d=>{
  387. d.workingTime = (d.progress*this.reportTimeType.allday/100).toFixed(1);
  388. });
  389. },
  390. cancel() {
  391. const toast = this.$toast.loading({
  392. forbidClick: true,
  393. duration: 0
  394. });
  395. var ids = '';
  396. var data = this.form.domains;
  397. data.forEach(element => {
  398. if (element.id != null && element.id != '') {
  399. ids +=(element.id+',');
  400. }
  401. });
  402. this.$axios.post("/report/cancel", {userId: this.user.id, reportIds:ids})
  403. .then(res => {
  404. if(res.code == "ok") {
  405. toast.clear();
  406. this.$toast.success('撤销成功');
  407. this.getReport();
  408. } else {
  409. toast.clear();
  410. this.$toast.fail('获取失败');
  411. }
  412. }).catch(err=> {toast.clear();});
  413. },
  414. confirmWorkTime(field) {
  415. if (field == 0) {
  416. this.curWorktime.startTime = this.startTime;
  417. this.showWorkStartTime = false;
  418. } else {
  419. this.curWorktime.endTime = this.endTime;
  420. this.showWorkEndTime = false;
  421. }
  422. },
  423. confirmTime(item, field) {
  424. if (field == 0) {
  425. item.startTime = this.startTime;
  426. this.showStartTime = false;
  427. } else {
  428. item.endTime = this.endTime;
  429. this.showEndTime = false;
  430. }
  431. },
  432. filter(type, options) {
  433. if (type === 'minute') {
  434. return options.filter(option => option % 30 === 0);
  435. }
  436. return options;
  437. },
  438. choseTimePick(value, index) {
  439. //选中时间
  440. if (this.reportTimeType.type == 0) {
  441. this.form.domains[this.clickTimeIndex].timeType = value.value;
  442. this.form.domains[this.clickTimeIndex].workingTime = value.hours;
  443. this.form.domains[this.clickTimeIndex].label = value.label;
  444. this.showPickerTime = false;
  445. } else if (this.reportTimeType.type == 1) {
  446. console.log('this.reportTimeType.type=='+value);
  447. this.form.domains[this.clickTimeIndex].workingTime = value;
  448. this.form.domains[this.clickTimeIndex].label = value.toFixed(1)+'小时';
  449. this.showPickerHours = false;
  450. }
  451. },
  452. clickTimePicker(i) {
  453. if (!this.canEdit) {
  454. return;
  455. }
  456. this.clickTimeIndex = i;
  457. if (this.reportTimeType.type == 0) {
  458. this.showPickerTime = true;
  459. } else if (this.reportTimeType.type == 1) {
  460. this.showPickerHours = true;
  461. }
  462. },
  463. getTimeType() {
  464. this.$axios.post('/time-type/getCompanyTimeSetting', { companyId: this.user.companyId})
  465. .then(res => {
  466. if(res.code == "ok") {
  467. var t = res.data;
  468. this.reportTimeType = t;
  469. //转化时间格式
  470. if (t.allday != null) {
  471. this.timeType.push({value:0, label:'全天 - '+t.allday+'小时', hours:t.allday});
  472. }
  473. if (t.am != null) {
  474. this.timeType.push({value:1, label:'上午 - '+t.am+'小时', hours: t.am});
  475. }
  476. if (t.pm != null) {
  477. this.timeType.push({value:2, label:'下午 - '+t.pm+'小时', hours: t.pm});
  478. }
  479. } else {
  480. toast.clear();
  481. this.$toast.fail(res.msg);
  482. }
  483. }).catch(err=> {toast.clear();});
  484. },
  485. // 返回
  486. back() {
  487. history.back();
  488. },
  489. // 时间转换
  490. format(date, pattern) {
  491. pattern = pattern || "yyyy-MM-dd";
  492. var _this = this;
  493. return pattern.replace(/([yMdhsm])(\1*)/g, function ($0) {
  494. switch ($0.charAt(0)) {
  495. case 'y': return _this.padding(date.getFullYear(), $0.length);
  496. case 'M': return _this.padding(date.getMonth() + 1, $0.length);
  497. case 'd': return _this.padding(date.getDate(), $0.length);
  498. case 'w': return date.getDay() + 1;
  499. case 'h': return _this.padding(date.getHours(), $0.length);
  500. case 'm': return _this.padding(date.getMinutes(), $0.length);
  501. case 's': return _this.padding(date.getSeconds(), $0.length);
  502. }
  503. });
  504. },
  505. padding(s, len) {
  506. var len = len - (s + '').length;
  507. for (var i = 0; i < len; i++) { s = '0' + s; }
  508. return s;
  509. },
  510. // 获取项目
  511. getProject() {
  512. const toast = this.$toast.loading({
  513. forbidClick: true,
  514. duration: 0
  515. });
  516. this.$axios.post("/project/getProjectList", {})
  517. .then(res => {
  518. if(res.code == "ok") {
  519. toast.clear();
  520. this.project = res.data;
  521. // if (this.project.length > 0) {
  522. // console.log('this.project[0].id=='+this.project[0].id);
  523. // this.getTaskList(this.project[0].id);
  524. // }
  525. } else {
  526. toast.clear();
  527. this.$toast.fail('获取失败:'+res.msg);
  528. }
  529. }).catch(err=> {toast.clear();});
  530. },
  531. // 获取日报
  532. getReport() {
  533. const toast = this.$toast.loading({
  534. forbidClick: true,
  535. duration: 0
  536. });
  537. this.$axios.post("/report/getReport", {date: this.form.createDate})
  538. .then(res => {
  539. if(res.code == "ok") {
  540. toast.clear();
  541. this.report = res.data;
  542. var t = res.data.timeType;
  543. var timeType=[];
  544. //转化时间格式
  545. if (t.allday != null) {
  546. timeType.push({value:0, label:'全天 - '+t.allday+'小时', hours:t.allday});
  547. }
  548. if (t.am != null) {
  549. timeType.push({value:1, label:'上午 - '+t.am+'小时', hours: t.am});
  550. }
  551. if (t.pm != null) {
  552. timeType.push({value:2, label:'下午 - '+t.pm+'小时', hours: t.pm});
  553. }
  554. var list = res.data.report;
  555. if(list.length != 0) {
  556. this.canEdit = false;
  557. this.canCancel = false;
  558. let array = [];
  559. for(var i in list) {
  560. var projectName = "";
  561. var flg = (list[i].isOvertime == 1);
  562. for(var j in this.project) {
  563. if(this.project[j].id == list[i].projectId) {
  564. projectName = this.project[j].projectName;
  565. }
  566. }
  567. let tname = '';
  568. if (list[i].taskId != null && list[i].taskList.length > 0) {
  569. let filterList = list[i].taskList.filter(t=>t.taskId == list[i].taskId);
  570. if (filterList.length > 0) {
  571. tname = filterList[0].taskName;
  572. }
  573. }
  574. array.push({
  575. id: list[i].id,
  576. projectId: list[i].projectId,
  577. projectName: projectName,
  578. workingTime: String(list[i].workingTime),
  579. content: list[i].content,
  580. state: list[i].state,
  581. timeType: list[i].timeType,
  582. label: timeType[list[i].timeType].label,
  583. startTime: list[i].startTime,
  584. endTime: list[i].endTime,
  585. isOvertime: flg,
  586. progress: list[i].progress,
  587. taskList: list[i].taskList,
  588. taskId: list[i].taskId,
  589. taskName:tname,
  590. professionProgress:list[i].professionProgressList,
  591. pics: list[i].pics,
  592. iospics:list[i].pics,
  593. multiWorktime:t.multiWorktime,
  594. worktimeList:list[i].worktimeList
  595. })
  596. if (list[i].state >= 2) {
  597. this.canEdit = true;
  598. } else if (list[i].state == 0) {
  599. this.canCancel = true;
  600. }
  601. }
  602. this.form.domains = array;
  603. } else {
  604. this.canCancel = false;
  605. this.canEdit = true;
  606. //没有填报的可以点击提交
  607. this.form.domains = [{
  608. id: null,
  609. projectId: "",
  610. projectName: "",
  611. workingTime: t.type==3?(t.allday).toFixed(1):"8.0",
  612. content: "",
  613. state: 2,
  614. progress:100,
  615. isOvertime:false,
  616. professionProgress:[],
  617. multiWorktime:t.multiWorktime,
  618. worktimeList:[{}]
  619. }]
  620. this.canEdit = true;
  621. }
  622. } else {
  623. toast.clear();
  624. this.$toast.fail('获取失败:'+res.msg);
  625. }
  626. }).catch(err=> {toast.clear();});
  627. },
  628. // 改变时间
  629. changeTime(time) {
  630. this.form.createDate = this.format(new Date(time),"yyyy-MM-dd");
  631. this.currentDate = time;
  632. this.showPicker = false;
  633. this.getReport();
  634. },
  635. // 选择项目
  636. clickPicker(i) {
  637. if (!this.canEdit) return;
  638. this.clickIndex = i;
  639. this.showPickerProject = true;
  640. },
  641. //选择任务
  642. clickPickerTask(i) {
  643. if (!this.canEdit) return;
  644. this.clickIndex = i;
  645. this.showPickerTask = true;
  646. },
  647. choseTask(value, index) {
  648. this.form.domains[this.clickIndex].taskId = value.taskId;
  649. this.form.domains[this.clickIndex].taskName = value.taskName;
  650. this.showPickerTask = false;
  651. },
  652. choseProject(value, index) {
  653. this.form.domains[this.clickIndex].projectId = value.id;
  654. this.form.domains[this.clickIndex].projectName = value.projectName;
  655. this.showPickerProject = false;
  656. this.getTaskList(value.id);
  657. //加载项目相关的工程进度
  658. if (this.user.company.packageEngineering == 1) {
  659. this.getProjectProfessions(this.form.domains[this.clickIndex], index);
  660. }
  661. },
  662. getProjectProfessions(domain, index) {
  663. this.$axios.post("/project-profession/getMyProfession", {projectId: domain.projectId})
  664. .then(res => {
  665. if(res.code == "ok") {
  666. this.form.domains[this.clickIndex].professionProgress = res.data;
  667. this.$forceUpdate();
  668. } else {
  669. this.$toast.fail('获取失败');
  670. }
  671. }).catch(err=> {toast.clear();});
  672. },
  673. getTaskList(projectId) {
  674. console.log('==============='+projectId);
  675. //如果是专业版,需要列出任务列表
  676. if (this.user.company.packageProject == 1) {
  677. this.$axios.post("/task/getRecentTask", {projectId: projectId})
  678. .then(res => {
  679. if(res.code == "ok") {
  680. this.form.domains[this.clickIndex].taskList = res.data;
  681. this.$forceUpdate();
  682. } else {
  683. this.$toast.fail('获取失败');
  684. }
  685. }).catch(err=> {toast.clear();});
  686. }
  687. },
  688. //添加工时
  689. addNewWorktime(index, item) {
  690. item.worktimeList.push({});
  691. },
  692. // 添加项目
  693. addNewPro() {
  694. var leftProgress = 10;
  695. if (this.reportTimeType.type == 3) {
  696. //计算已经待分配工时比例
  697. let array = this.form.domains;
  698. let totalProgress = 0;
  699. for (var i=0;i<array.length; i++) {
  700. totalProgress += array[i].progress;
  701. }
  702. if (totalProgress < 100) {
  703. leftProgress = 100 - totalProgress;
  704. }
  705. }
  706. this.form.domains.push({
  707. id: null,
  708. projectId: "",
  709. projectName: "",
  710. workingTime: this.reportTimeType.type==3?(leftProgress*this.reportTimeType.allday/100).toFixed(1):"4.0",
  711. progress:leftProgress,
  712. content: "",
  713. state: 2,
  714. isOvertime:false,
  715. multiWorktime: this.reportTimeType.multiWorktime,
  716. worktimeList:[{}]
  717. })
  718. },
  719. // 移除项目
  720. delPro(i) {
  721. this.$dialog.confirm({
  722. title: '',
  723. message: '是否移除当前项目'
  724. }).then(() => {
  725. this.form.domains.splice(i,1);
  726. }).catch(() => {
  727. });
  728. },
  729. // 提交日报
  730. register() {
  731. const toast = this.$toast.loading({
  732. forbidClick: true,
  733. duration: 0
  734. });
  735. let formData = new FormData();
  736. if (this.reportTimeType.type == 0) {
  737. var alldayNum = 0;
  738. var amNum = 0;
  739. var pmNum = 0;
  740. for(var i in this.form.domains) {
  741. if (this.form.domains[i].timeType == 0) {
  742. alldayNum ++;
  743. } else if (this.form.domains[i].timeType == 1) {
  744. amNum++;
  745. } else if (this.form.domains[i].timeType == 2) {
  746. pmNum++;
  747. }
  748. }
  749. if (alldayNum > 1) {
  750. this.$toast.fail("工作时间-全天,只能选择一次");
  751. return;
  752. }
  753. if (amNum > 1) {
  754. this.$toast.fail("工作时间-上午,只能选择一次");
  755. return;
  756. }
  757. if (pmNum > 1) {
  758. this.$toast.fail("工作时间-下午,只能选择一次");
  759. return;
  760. }
  761. if (alldayNum == 1 && (amNum > 0 || pmNum > 0)) {
  762. this.$toast.fail("工作时间-全天,不能和上下午同时存在");
  763. return;
  764. }
  765. } else if (this.reportTimeType.type == 3) {
  766. //总百分比不能超过100%
  767. let total = 0;
  768. this.form.domains.forEach(w=>{total += w.progress});
  769. if (total > 100) {
  770. this.$toast.fail("用时比例之和不能超过100%");
  771. return;
  772. } else if (total < 100) {
  773. this.$toast.fail("工时尚未完全分配,无法提交");
  774. return;
  775. }
  776. }
  777. //填字段
  778. for(var i in this.form.domains) {
  779. if (this.form.domains[i].id != null) {
  780. formData.append("id", this.form.domains[i].id);
  781. } else {
  782. formData.append("id", -1);
  783. }
  784. formData.append("projectId", parseFloat(this.form.domains[i].projectId));
  785. if (this.form.domains[i].subProjectId != null) {
  786. formData.append("subProjectId", this.form.domains[i].subProjectId);
  787. } else {
  788. formData.append("subProjectId", 0);
  789. }
  790. formData.append("reportTimeType", this.reportTimeType.type);
  791. formData.append("multiWorktime", this.reportTimeType.multiWorktime);
  792. if (this.reportTimeType.type == 0) {
  793. formData.append("timeType", this.form.domains[i].timeType);
  794. formData.append("workingTime", parseFloat(this.form.domains[i].workingTime));
  795. } else if (this.reportTimeType.type == 1) {
  796. formData.append("workingTime", parseFloat(this.form.domains[i].workingTime));
  797. } else if (this.reportTimeType.type == 2) {
  798. formData.append("startTime", this.form.domains[i].startTime);
  799. formData.append("endTime",this.form.domains[i].endTime);
  800. } else if (this.reportTimeType.type == 3) {
  801. formData.append("progress", this.form.domains[i].progress);
  802. formData.append("workingTime",this.form.domains[i].workingTime);
  803. }
  804. if (this.form.domains[i].taskId == null) {
  805. formData.append("taskId", 0);
  806. } else {
  807. formData.append("taskId", this.form.domains[i].taskId);
  808. }
  809. formData.append("createDate", this.form.createDate);
  810. formData.append("isOvertime", this.form.domains[i].isOvertime?1:0);
  811. //项目专业进度
  812. if (this.form.domains[i].professionProgress != null) {
  813. let m = JSON.stringify(this.form.domains[i].professionProgress);
  814. m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
  815. formData.append("professionProgress", m);
  816. } else {
  817. formData.append("professionProgress", "[]");
  818. }
  819. //图片的处理,只处理当前通过手机拍摄的新的照片
  820. if (this.form.domains[i].serverPics!= null && this.form.domains[i].serverPics.length > 0) {
  821. let m = JSON.stringify(this.form.domains[i].serverPics);
  822. m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
  823. formData.append("pics", m);
  824. } else {
  825. formData.append("pics", "@");
  826. }
  827. //处理多个时间事项
  828. if (this.reportTimeType.multiWorktime == 1) {
  829. //检查时间是否有重叠
  830. var workList = this.form.domains[i].worktimeList;
  831. for (var j=0;j<workList.length; j++) {
  832. var curItem = workList[j];
  833. if (curItem.startTime == null || curItem.endTime == null) {
  834. this.$toast.fail("请设置工作时间");
  835. return;
  836. }
  837. //检查开始时间是否大于结束时间
  838. if (curItem.startTime >= curItem.endTime) {
  839. this.$toast.fail("时间段"+curItem.startTime+'-'+curItem.endTime+"有误:"+
  840. "结束时间必须大于开始时间");
  841. return;
  842. }
  843. for (var p = j+1;p<workList.length; p++) {
  844. var jItem = workList[p];
  845. if ((jItem.startTime>=curItem.startTime&&jItem.startTime < curItem.endTime)
  846. || (jItem.endTime>curItem.startTime&&jItem.endTime <= curItem.endTime)) {
  847. this.$toast.fail("时间段"+curItem.startTime+'-'+curItem.endTime+"与"+
  848. jItem.startTime+'-'+jItem.endTime+ "存在重叠,请修改。");
  849. return;
  850. }
  851. }
  852. }
  853. let m = JSON.stringify(this.form.domains[i].worktimeList);
  854. m = m.replace(/,/g,"@");//replaceAll(',','@');企业微信不兼容replaceAll
  855. formData.append("content", m);
  856. } else {
  857. if (this.form.domains[i].content == null || this.form.domains[i].content == '') {
  858. formData.append("content", '-');
  859. } else {
  860. formData.append("content", this.form.domains[i].content);
  861. }
  862. }
  863. }
  864. this.$axios.post("/report/editReport", formData)
  865. .then(res => {
  866. if(res.code == "ok") {
  867. toast.clear();
  868. this.$toast.success('填报成功');
  869. this.$router.push("/index");
  870. } else {
  871. toast.clear();
  872. this.$toast.fail('填报失败');
  873. }
  874. }).catch(err=> {toast.clear();});
  875. },
  876. //初始化参数
  877. initWxConfig() {
  878. var curUrl=location.href.split("#")[0];
  879. this.$axios.post("/wxcorp/getCorpWXConfig", {url: curUrl, token: this.user.id})
  880. .then(res => {
  881. if(res.code == "ok") {
  882. var data=res.data;
  883. wx.config({
  884. beta: true,
  885. debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  886. appId: data.appid, // 必填,公众号的唯一标识
  887. timestamp: data.timestamp, // 必填,生成签名的时间戳
  888. nonceStr: data.noncestr, // 必填,生成签名的随机串
  889. signature: data.sign, // 必填,签名,见附录1
  890. jsApiList: [
  891. 'chooseImage',
  892. 'previewImage',
  893. 'uploadImage',
  894. 'downloadImage',
  895. 'previewFile',
  896. 'getLocation',
  897. ]
  898. });
  899. wx.ready(function(){
  900. // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
  901. console.log('企业微信初始化执行成功了。 ');
  902. });
  903. } else {
  904. this.$toast.fail('获取失败');
  905. }
  906. }).catch(err=> {this.$toast.fail('发生异常'+err);console.log(err)});
  907. }
  908. },
  909. mounted() {
  910. var ua = navigator.userAgent.toLowerCase();
  911. this.isIOSystem = this.isIOS();
  912. if (ua.indexOf("wxwork") > 0) {
  913. this.isCorpWX = true;
  914. } else if (ua.indexOf("micromessenger") > 0) {
  915. this.isWX = true;
  916. }
  917. //获取传递过来的日期
  918. var passDate = this.$route.query.date;
  919. if (passDate != null) {
  920. this.form.createDate = this.$route.query.date;
  921. }
  922. this.getProject();
  923. this.getReport();
  924. this.getTimeType();
  925. //初始化微信js-sdk参数
  926. if (this.isCorpWX) {
  927. this.initWxConfig();
  928. }
  929. }
  930. };
  931. </script>
  932. <style lang="less" scope>
  933. .my-swipe .van-swipe-item {
  934. color: #fff;
  935. font-size: 20px;
  936. line-height: 150px;
  937. text-align: center;
  938. background-color: #39a9ed;
  939. }
  940. .van-field__control:disabled {
  941. -webkit-text-fill-color:#646566;
  942. }
  943. .login_form {
  944. margin-top: 46px;
  945. }
  946. .form_domains {
  947. position: relative;
  948. .form_copy {
  949. position: absolute;
  950. right: 85px;
  951. top: 10px;
  952. }
  953. .form_addNew {
  954. text-align: center;
  955. margin:10px;
  956. }
  957. .form_del {
  958. position: absolute;
  959. right: 10px;
  960. top: 10px;
  961. }
  962. // .form_del {
  963. // color: #ff0000;
  964. // font-size: 22px;
  965. // position: absolute;
  966. // right: 15px;
  967. // top: 10px;
  968. // }
  969. }
  970. .form_tip {
  971. text-align: center;
  972. margin-top: 20px;
  973. color: #afafaf;
  974. font-size: 14px;
  975. }
  976. .card__footer {
  977. padding-top: 10px;
  978. }
  979. .card__tags {
  980. .van-tag {
  981. margin-right: 5px;
  982. }
  983. }
  984. </style>
  985. <style lang="less">
  986. .van-nav-bar .van-icon , .van-nav-bar__text {
  987. color: #20a0ff;
  988. }
  989. .overtime {
  990. height: 50px;
  991. box-sizing: border-box;
  992. padding-left: 10px;
  993. display: flex;
  994. align-items: center;
  995. }
  996. .custom-button {
  997. width: 40px;
  998. color: #fff;
  999. font-size: 10px;
  1000. line-height: 16px;
  1001. text-align: center;
  1002. background-color: #1989fa;
  1003. border-radius: 100px;
  1004. }
  1005. </style>