checkIn.vue 13 KB

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