checkIn.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. <template>
  2. <view class="home">
  3. <view class="nav">
  4. <view :style="{ paddingTop }">
  5. <view class="title" :style="[{ height }, { lineHeight: height }]">
  6. <view class="back" @click="goBack()">
  7. <u-icon name="arrow-left" color="#ffffff" size="22"></u-icon>
  8. </view>
  9. <text>打卡</text>
  10. </view>
  11. </view>
  12. </view>
  13. <view class="main">
  14. <view class="main-header">
  15. <view class="user-info">
  16. <image class="user-img" src="@/static/images/user.jpg" mode="scaleToFill" />
  17. <view class="info flex1 flex-column flex_1">
  18. <u-text :lines="1" size="32rpx" :bold="true" :text="nickName"></u-text>
  19. <u-text :lines="1" size="24rpx" :text="postName"></u-text>
  20. </view>
  21. </view>
  22. <view class="more" @click="showCheckList">
  23. <u-icon name="order" size="60rpx"></u-icon>
  24. <u-text :lines="1" size="24rpx" text="打卡记录"></u-text>
  25. </view>
  26. </view>
  27. <view class="main-form" v-if="!showList">
  28. <u-form :model="form" :rules="rules" ref="form" label-width="0">
  29. <u-form-item prop="punchVal" borderBottom customStyle="padding:40rpx 0 30rpx" @click="showCheckType = true">
  30. <view class="form-label flex_l">
  31. <view class="label-tag"></view>
  32. 打卡类型
  33. </view>
  34. <u-input :readonly="true" placeholder="请选择销售模式" v-model="form.punchVal" border="none"
  35. suffixIcon="arrow-down" suffixIconStyle="color:#CDCDCD" clearable customStyle="padding: 0 30rpx 0 12rpx">
  36. </u-input>
  37. </u-form-item>
  38. <u-form-item prop="targetName" borderBottom customStyle="padding:40rpx 0 30rpx" v-if="form.punchType > 10"
  39. @click="openSelectUser()">
  40. <view class="form-label flex_l">
  41. <view class="label-tag"></view>
  42. {{ form.punchType == 20 ? '客户' : '经销商' }}
  43. </view>
  44. <u-input :readonly="true" :placeholder="`请选择${form.punchType == 20 ? '客户' : '经销商'}`"
  45. v-model="form.targetName" border="none" suffixIcon="arrow-down" suffixIconStyle="color:#CDCDCD" clearable
  46. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  47. </u-form-item>
  48. </u-form>
  49. </view>
  50. <view class="main-container">
  51. <template v-if="!showList">
  52. <view class="btn" @click="checkIn">打卡</view>
  53. <view class="location">
  54. <u-text @click="getLocation" :lines="1" :block="true" align="center" size="32rpx" :text="address"></u-text>
  55. <u-loading-icon mode="circle" size="24rpx" :show="show"></u-loading-icon>
  56. </view>
  57. </template>
  58. <u-list height="calc(100vh - 410rpx)" @scrolltolower="scrolltolower" v-else>
  59. <u-list-item v-for="(item, index) in checkList" :key="index">
  60. <u-cell :title="item.punchTime" :label="item.punchLocation"></u-cell>
  61. </u-list-item>
  62. </u-list>
  63. </view>
  64. </view>
  65. <u-toast ref="uToast"></u-toast>
  66. <!-- <uni-file-picker file-mediatype="all" v-model="imageValue" @select="select" @progress="progress" @success="success"
  67. @fail="fail" ref="upload" limit="1">
  68. <view class="upload-btn">
  69. <u-icon name="plus-circle-fill" color="blue" size="28"></u-icon>
  70. </view>
  71. </uni-file-picker> -->
  72. <!-- 打卡类型 -->
  73. <u-picker :show="showCheckType" :columns="[checkTypeColumns]" keyName="value" @cancel="showCheckType = false"
  74. @confirm="pickCheckType"></u-picker>
  75. <!-- 选择经销商 -->
  76. <select-dealer ref="dealer" @close="closeDealer($event)"></select-dealer>
  77. <!-- 选择客户 -->
  78. <select-customer ref="cust" @close="closeCust($event)"></select-customer>
  79. <u-notify ref="uNotify"></u-notify>
  80. </view>
  81. </template>
  82. <script>
  83. import {
  84. mapGetters
  85. } from 'vuex'
  86. import to from 'await-to-js'
  87. import api from '@/api/system'
  88. import SelectDealer from 'components/SelectDealer'
  89. import SelectCustomer from 'components/SelectCustomer'
  90. export default {
  91. components: {
  92. SelectDealer,
  93. SelectCustomer
  94. },
  95. data() {
  96. return {
  97. height: '',
  98. paddingTop: '',
  99. address: '获取定位',
  100. showCheckType: false,
  101. form: {
  102. targetId: undefined, //经销商/代理商ID
  103. targetName: undefined, //经销商/代理商名称
  104. punchType: '', //打卡类型
  105. punchVal: '',
  106. punchLat: 0,
  107. punchLng: 0,
  108. punchLocation: '',
  109. punchImg: '',
  110. },
  111. show: false,
  112. imageValue: [],
  113. showList: false,
  114. checkList: [],
  115. queryForm: {
  116. pageNum: 1,
  117. pageSize: 10,
  118. },
  119. total: 0,
  120. checkTypeColumns: [{
  121. key: '10',
  122. value: '居家'
  123. },
  124. {
  125. key: '20',
  126. value: '客户'
  127. },
  128. {
  129. key: '30',
  130. value: '经销商'
  131. },
  132. // { key: 40, value: '代理商' },
  133. ],
  134. rules: {
  135. punchVal: [{
  136. required: true,
  137. trigger: ['blur', 'change'],
  138. message: '请选择打卡类型'
  139. }],
  140. targetName: [{
  141. required: true,
  142. trigger: ['blur', 'change'],
  143. message: `请选择用户`,
  144. }, ],
  145. },
  146. }
  147. },
  148. computed: {
  149. ...mapGetters(['nickName', 'postName']),
  150. },
  151. created() {
  152. this.getLocation()
  153. const navData = uni.getMenuButtonBoundingClientRect()
  154. this.height = navData.height + 'px'
  155. this.paddingTop = navData.top + 'px'
  156. },
  157. watch: {
  158. 'form.punchType'(val) {
  159. this.form.targetId = undefined
  160. this.form.targetName = undefined
  161. },
  162. },
  163. methods: {
  164. // 选择打卡类型
  165. pickCheckType(e) {
  166. this.form.punchType = e.value[0].key
  167. this.form.punchVal = e.value[0].value
  168. this.showCheckType = false
  169. },
  170. // 经销商
  171. closeDealer(dealer) {
  172. if (dealer) {
  173. this.form.targetId = dealer.id
  174. this.form.targetName = dealer.name
  175. }
  176. },
  177. // 关闭选择客户
  178. closeCust(user) {
  179. if (user) {
  180. this.form.targetId = user.id
  181. this.form.targetName = user.name
  182. }
  183. },
  184. openSelectUser() {
  185. if (this.form.punchType == 30) {
  186. this.$refs.dealer.open()
  187. } else {
  188. this.$refs.cust.open()
  189. }
  190. },
  191. getLocation() {
  192. this.show = true
  193. const _this = this
  194. uni.getLocation({
  195. type: 'wgs84',
  196. success: function(res) {
  197. const latitude = res.latitude.toString()
  198. const longitude = res.longitude.toString()
  199. uni.request({
  200. header: {
  201. 'Content-Type': 'application/text',
  202. },
  203. url: process.uniEnv.VUE_APP_PROTOCOL +
  204. 'apis.map.qq.com/ws/geocoder/v1/?location=' +
  205. latitude +
  206. ',' +
  207. longitude +
  208. '&key=6REBZ-BRN6J-BMAFY-FIRI3-SMK43-G3FXU',
  209. success(re) {
  210. if (re.statusCode === 200) {
  211. _this.address = re.data.result.address
  212. _this.form.punchLat = latitude
  213. _this.form.punchLng = longitude
  214. _this.form.punchLocation = re.data.result.address
  215. _this.show = false
  216. } else {
  217. _this.address = '点击重试'
  218. this.$refs.uToast.show({
  219. type: 'error',
  220. message: '定位失败,请重试',
  221. })
  222. }
  223. },
  224. fail: (err) => (_this.show = false),
  225. })
  226. },
  227. fail: (err) => (_this.show = false),
  228. })
  229. },
  230. async checkIn() {
  231. const [fail, resp] = await to(this.$refs.form.validate())
  232. if (fail && fail.length > 0) {
  233. this.$refs.uNotify.show({
  234. top: this.height + this.paddingTop + 10,
  235. type: 'warning',
  236. message: fail[0].message,
  237. duration: 1000 * 3,
  238. })
  239. return
  240. }
  241. if (!this.form.punchLat || !this.form.punchLng) {
  242. this.address = '点击重试'
  243. return this.$refs.uToast.show({
  244. type: 'error',
  245. message: '定位失败,请重试',
  246. })
  247. }
  248. // console.log(this.$refs.upload);
  249. // this.$refs.upload.choose()
  250. const [err, res] = await uni.chooseImage({
  251. count: 1, //默认9
  252. sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
  253. sourceType: ['camera'],
  254. })
  255. // console.log(res, 'rs');
  256. if (err) return
  257. this.select(res)
  258. res.tempFiles[0].name = new Date().getTime() + ''
  259. res.tempFiles[0].type = ''
  260. // console.log(res.tempFiles[0], typeof res.tempFiles[0])
  261. },
  262. // // 选择上传触发函数
  263. select(e) {
  264. let url = process.uniEnv.VUE_APP_UPLOAD_WEED
  265. uni.request({
  266. url, //仅为示例,并非真实接口地址。
  267. success: (res) => {
  268. this.uploadFiles(e, res.data)
  269. },
  270. })
  271. },
  272. // 上传函数
  273. async uploadFiles(e, res) {
  274. let action = process.uniEnv.VUE_APP_PROTOCOL + res.publicUrl + '/' + res.fid
  275. uni.uploadFile({
  276. url: action,
  277. filePath: e.tempFilePaths[0],
  278. name: 'file',
  279. formData: {
  280. file: e.tempFiles[0],
  281. },
  282. success: (res) => {
  283. this.form.punchImg = action
  284. api.checkIn(this.form).then((res) => {
  285. this.$refs.uToast.show({
  286. type: 'success',
  287. message: '打卡成功',
  288. })
  289. })
  290. },
  291. fail: () => {
  292. console.log('err')
  293. },
  294. })
  295. },
  296. showCheckList() {
  297. if (!this.showList) {
  298. this.queryForm.pageNum = 1
  299. this.checkList = []
  300. this.loadCheckList()
  301. }
  302. this.showList = !this.showList
  303. },
  304. async loadCheckList() {
  305. this.loadStatus = 'loading'
  306. const [err, res] = await to(api.getCheckList(this.queryForm))
  307. if (err) {
  308. this.loadStatus = 'nomore'
  309. return
  310. }
  311. if (res && res.code == 200) {
  312. this.checkList = [...this.checkList, ...(res.data.list || [])]
  313. // this.checkList = res.data.list
  314. this.total = res.data.total
  315. this.loadStatus = this.checkList.length == this.total ? 'nomore' : 'loadmore'
  316. } else {
  317. this.loadStatus = 'nomore'
  318. }
  319. },
  320. scrolltolower() {
  321. if (this.checkList.length < this.total && this.loadStatus != 'loading') {
  322. this.queryForm.pageNum++
  323. this.$u.throttle(this.loadCheckList(), 2000, false)
  324. }
  325. },
  326. goBack() {
  327. uni.navigateBack({
  328. //关闭当前页面,返回上一页面或多级页面。
  329. delta: 1,
  330. })
  331. },
  332. },
  333. }
  334. </script>
  335. <style lang="scss" scoped>
  336. /deep/ .uni-file-picker {
  337. display: none;
  338. }
  339. .home {
  340. padding-top: 188rpx;
  341. height: 100%;
  342. .nav {
  343. position: absolute;
  344. left: 0;
  345. top: 0;
  346. width: 100%;
  347. height: 284rpx;
  348. background: #3e7ef8;
  349. .title {
  350. position: relative;
  351. text-align: center;
  352. font-size: 32rpx;
  353. font-weight: bold;
  354. color: #ffffff;
  355. .back {
  356. position: absolute;
  357. top: 0;
  358. bottom: 0;
  359. margin: auto;
  360. left: 70rpx;
  361. display: flex;
  362. }
  363. }
  364. }
  365. .main {
  366. display: flex;
  367. flex-direction: column;
  368. position: absolute;
  369. width: 100%;
  370. height: calc(100vh - 188rpx);
  371. background: #ffffff;
  372. box-shadow: 0 6rpx 19rpx 2rpx rgba(0, 45, 132, 0.15);
  373. border-radius: 31rpx 31rpx 0 0;
  374. padding: 0 32rpx;
  375. overflow: hidden;
  376. padding-bottom: 64rpx;
  377. .main-header {
  378. padding: 20rpx 40rpx;
  379. display: flex;
  380. justify-content: space-between;
  381. .user-info {
  382. display: flex;
  383. .user-img {
  384. width: 116rpx;
  385. height: 116rpx;
  386. border-radius: 50%;
  387. margin-right: 32rpx;
  388. }
  389. .info {
  390. height: 116rpx;
  391. margin-right: 32rpx;
  392. }
  393. }
  394. .more {
  395. display: flex;
  396. flex-direction: column;
  397. align-items: center;
  398. }
  399. }
  400. .main-container {
  401. flex: 1;
  402. display: flex;
  403. flex-direction: column;
  404. align-items: center;
  405. justify-content: center;
  406. .btn {
  407. width: 300rpx;
  408. height: 300rpx;
  409. border-radius: 50%;
  410. background: linear-gradient(70deg, #3e7ef8, #1d66dc);
  411. display: flex;
  412. justify-content: center;
  413. align-items: center;
  414. color: #fff;
  415. font-size: 48rpx;
  416. font-weight: bold;
  417. box-shadow: 0 6rpx 19rpx 2rpx rgba(29, 102, 220, 0.2);
  418. margin-bottom: 40rpx;
  419. }
  420. .location {
  421. display: flex;
  422. height: 32rpx;
  423. line-height: 32rpx;
  424. }
  425. }
  426. }
  427. }
  428. </style>