checkIn.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438
  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. const [err, res] = await uni.chooseImage({
  249. count: 1, //默认9
  250. sizeType: ['original'], //可以指定是原图还是压缩图,默认二者都有
  251. sourceType: ['camera'],
  252. })
  253. if (err) return
  254. this.select(res)
  255. res.tempFiles[0].name = new Date().getTime() + ''
  256. res.tempFiles[0].type = ''
  257. },
  258. // // 选择上传触发函数
  259. select(e) {
  260. let url = process.uniEnv.VUE_APP_UPLOAD_WEED
  261. uni.request({
  262. url, //仅为示例,并非真实接口地址。
  263. success: (res) => {
  264. this.uploadFiles(e, res.data)
  265. },
  266. })
  267. },
  268. // 上传函数
  269. async uploadFiles(e, res) {
  270. let action = process.uniEnv.VUE_APP_PROTOCOL + res.publicUrl + '/' + res.fid
  271. uni.uploadFile({
  272. url: action,
  273. filePath: e.tempFilePaths[0],
  274. name: 'file',
  275. formData: {
  276. file: e.tempFiles[0],
  277. },
  278. success: (res) => {
  279. this.form.punchImg = action
  280. api.checkIn(this.form).then((res) => {
  281. this.$refs.uToast.show({
  282. type: 'success',
  283. message: '打卡成功',
  284. })
  285. })
  286. },
  287. fail: () => {
  288. console.log('err')
  289. },
  290. })
  291. },
  292. showCheckList() {
  293. if (!this.showList) {
  294. this.queryForm.pageNum = 1
  295. this.checkList = []
  296. this.loadCheckList()
  297. }
  298. this.showList = !this.showList
  299. },
  300. async loadCheckList() {
  301. this.loadStatus = 'loading'
  302. const [err, res] = await to(api.getCheckList(this.queryForm))
  303. if (err) {
  304. this.loadStatus = 'nomore'
  305. return
  306. }
  307. if (res && res.code == 200) {
  308. this.checkList = [...this.checkList, ...(res.data.list || [])]
  309. // this.checkList = res.data.list
  310. this.total = res.data.total
  311. this.loadStatus = this.checkList.length == this.total ? 'nomore' : 'loadmore'
  312. } else {
  313. this.loadStatus = 'nomore'
  314. }
  315. },
  316. scrolltolower() {
  317. if (this.checkList.length < this.total && this.loadStatus != 'loading') {
  318. this.queryForm.pageNum++
  319. this.$u.throttle(this.loadCheckList(), 2000, false)
  320. }
  321. },
  322. goBack() {
  323. uni.navigateBack({
  324. //关闭当前页面,返回上一页面或多级页面。
  325. delta: 1,
  326. })
  327. },
  328. },
  329. }
  330. </script>
  331. <style lang="scss" scoped>
  332. /deep/ .uni-file-picker {
  333. display: none;
  334. }
  335. .home {
  336. padding-top: 188rpx;
  337. height: 100%;
  338. .nav {
  339. position: absolute;
  340. left: 0;
  341. top: 0;
  342. width: 100%;
  343. height: 284rpx;
  344. background: #3e7ef8;
  345. .title {
  346. position: relative;
  347. text-align: center;
  348. font-size: 32rpx;
  349. font-weight: bold;
  350. color: #ffffff;
  351. .back {
  352. position: absolute;
  353. top: 0;
  354. bottom: 0;
  355. margin: auto;
  356. left: 70rpx;
  357. display: flex;
  358. }
  359. }
  360. }
  361. .main {
  362. display: flex;
  363. flex-direction: column;
  364. position: absolute;
  365. width: 100%;
  366. height: calc(100vh - 188rpx);
  367. background: #ffffff;
  368. box-shadow: 0 6rpx 19rpx 2rpx rgba(0, 45, 132, 0.15);
  369. border-radius: 31rpx 31rpx 0 0;
  370. padding: 0 32rpx;
  371. overflow: hidden;
  372. padding-bottom: 64rpx;
  373. .main-header {
  374. padding: 20rpx 40rpx;
  375. display: flex;
  376. justify-content: space-between;
  377. .user-info {
  378. display: flex;
  379. .user-img {
  380. width: 116rpx;
  381. height: 116rpx;
  382. border-radius: 50%;
  383. margin-right: 32rpx;
  384. }
  385. .info {
  386. height: 116rpx;
  387. margin-right: 32rpx;
  388. }
  389. }
  390. .more {
  391. display: flex;
  392. flex-direction: column;
  393. align-items: center;
  394. }
  395. }
  396. .main-container {
  397. flex: 1;
  398. display: flex;
  399. flex-direction: column;
  400. align-items: center;
  401. justify-content: center;
  402. .btn {
  403. width: 300rpx;
  404. height: 300rpx;
  405. border-radius: 50%;
  406. background: linear-gradient(70deg, #3e7ef8, #1d66dc);
  407. display: flex;
  408. justify-content: center;
  409. align-items: center;
  410. color: #fff;
  411. font-size: 48rpx;
  412. font-weight: bold;
  413. box-shadow: 0 6rpx 19rpx 2rpx rgba(29, 102, 220, 0.2);
  414. margin-bottom: 40rpx;
  415. }
  416. .location {
  417. display: flex;
  418. height: 32rpx;
  419. line-height: 32rpx;
  420. }
  421. }
  422. }
  423. }
  424. </style>