follow.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. <!--
  2. * @Author: liuzhenlin 461480418@qq.ocm
  3. * @Date: 2023-01-12 11:57:48
  4. * @LastEditors: liuzhenlin
  5. * @LastEditTime: 2023-07-03 10:43:41
  6. * @Description: file content
  7. * @FilePath: \oms\pages\publicPages\follow.vue
  8. -->
  9. <template>
  10. <view class="home">
  11. <view class="nav">
  12. <view :style="{ paddingTop }">
  13. <view class="title" :style="[{ height }, { lineHeight: height }]">
  14. <view class="back" @click="goBack()">
  15. <u-icon name="arrow-left" color="#ffffff" size="22"></u-icon>
  16. </view>
  17. <text>填写跟进</text>
  18. </view>
  19. </view>
  20. </view>
  21. <view class="main">
  22. <u-form :model="addForm" :rules="rules" ref="addForm" label-width="0">
  23. <u-form-item prop="content" customStyle="padding:40rpx 0 34rpx">
  24. <view class="form-label flex_l">
  25. <view class="label-tag"></view>
  26. 本次跟进内容
  27. </view>
  28. <u-textarea
  29. v-model="addForm.content"
  30. placeholder="请输入本次跟进内容"
  31. maxlength="500"
  32. height="150rpx"
  33. count
  34. customStyle="border-radius:16rpx"></u-textarea>
  35. </u-form-item>
  36. <u-form-item prop="effect" customStyle="padding:40rpx 0 34rpx">
  37. <view class="form-label flex_l">
  38. <view class="label-tag"></view>
  39. 达成效果
  40. </view>
  41. <u-textarea
  42. v-model="addForm.effect"
  43. placeholder="请输入达成效果"
  44. maxlength="500"
  45. height="150rpx"
  46. count
  47. customStyle="border-radius:16rpx"></u-textarea>
  48. </u-form-item>
  49. <u-form-item prop="issue" customStyle="padding:40rpx 0 34rpx">
  50. <view class="form-label flex_l">
  51. <view class="label-tag"></view>
  52. 问题或困难
  53. </view>
  54. <u-textarea
  55. v-model="addForm.issue"
  56. placeholder="请输入问题或困难"
  57. maxlength="500"
  58. height="150rpx"
  59. count
  60. customStyle="border-radius:16rpx"></u-textarea>
  61. </u-form-item>
  62. <u-form-item prop="furtherPlan" customStyle="padding:40rpx 0 34rpx">
  63. <view class="form-label flex_l">
  64. <view class="label-tag"></view>
  65. 下一步跟进计划和目标
  66. </view>
  67. <u-textarea
  68. v-model="addForm.furtherPlan"
  69. placeholder="请输入下一步跟进计划和目标"
  70. maxlength="500"
  71. height="150rpx"
  72. count
  73. customStyle="border-radius:16rpx"></u-textarea>
  74. </u-form-item>
  75. <u-form-item prop="date" borderBottom customStyle="padding:40rpx 0 30rpx" @click="showDate = true">
  76. <view class="form-label flex_l">
  77. <view class="label-tag"></view>
  78. 跟进时间
  79. </view>
  80. <u-input
  81. :readonly="true"
  82. placeholder="请选择跟进时间"
  83. v-model="addForm.date"
  84. border="none"
  85. suffixIcon="arrow-down"
  86. suffixIconStyle="color:#CDCDCD"
  87. clearable
  88. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  89. </u-form-item>
  90. <u-form-item prop="mode" borderBottom customStyle="padding:40rpx 0 30rpx" @click="showMode = true">
  91. <view class="form-label flex_l">
  92. <view class="label-tag"></view>
  93. 跟进方式
  94. </view>
  95. <u-input
  96. :readonly="true"
  97. placeholder="请选择跟进方式"
  98. v-model="addForm.mode"
  99. border="none"
  100. suffixIcon="arrow-down"
  101. suffixIconStyle="color:#CDCDCD"
  102. clearable
  103. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  104. </u-form-item>
  105. <u-form-item prop="contactsName" borderBottom customStyle="padding:40rpx 0 30rpx" @click="open">
  106. <view class="form-label flex_l">
  107. <view class="label-tag"></view>
  108. 联系人
  109. </view>
  110. <u-input
  111. :readonly="true"
  112. placeholder="请选择联系人"
  113. v-model="addForm.contactsName"
  114. border="none"
  115. suffixIcon="arrow-down"
  116. suffixIconStyle="color:#CDCDCD"
  117. clearable
  118. customStyle="padding: 0 30rpx 0 12rpx"></u-input>
  119. </u-form-item>
  120. </u-form>
  121. <view class="upload-file-box">
  122. <view class="form-label flex_l">附件</view>
  123. <uni-file-picker file-mediatype="all" v-model="imageValue" mode="grid" @select="select" ref="upload" limit="1">
  124. <view class="upload-btn">
  125. <u-icon name="plus-circle-fill" color="blue" size="28"></u-icon>
  126. </view>
  127. </uni-file-picker>
  128. </view>
  129. <view class="save" @click="handleAdd" :class="!flag ? 'disabledBtn' : ''">保存</view>
  130. <!-- 选择跟进方式 -->
  131. <u-picker
  132. :show="showMode"
  133. :columns="modeColumns"
  134. keyName="label"
  135. @cancel="showMode = false"
  136. @confirm="pickMode"></u-picker>
  137. <!-- 选择下次时间 -->
  138. <u-datetime-picker
  139. v-if="showDate"
  140. :maxDate="maxDate"
  141. :show="showDate"
  142. mode="datetime"
  143. v-model="addForm.date"
  144. @cancel="showDate = false"
  145. @confirm="pickDate"></u-datetime-picker>
  146. </view>
  147. <u-notify ref="uNotify"></u-notify>
  148. <u-toast ref="uToast"></u-toast>
  149. <!-- 客户联系人 -->
  150. <customer-contact ref="concat" @close="closeConcat"></customer-contact>
  151. <!-- 项目联系人 -->
  152. <project-contact ref="projectConcat" @close="closeConcat"></project-contact>
  153. <!-- 经销商代理商联系人 -->
  154. <distr-contact ref="distrConcat" @close="closeConcat" :distrType="targetType"></distr-contact>
  155. <!-- 联系人 -->
  156. <select-user ref="userConcat" @close="closeConcat"></select-user>
  157. </view>
  158. </template>
  159. <script>
  160. // import { mapGetters } from 'vuex'
  161. import to from 'await-to-js'
  162. import followApi from '../../api/follow'
  163. import distrApi from '../../api/base/distr'
  164. import custApi from '../../api/customer'
  165. import prodApi from '../../api/project'
  166. import bidApi from '../../api/bid'
  167. import partnerApi from '../../api/partner'
  168. import ProjectContact from '@/components/ProjectContact'
  169. import CustomerContact from '@/components/CustomerContact'
  170. import DistrContact from '@/components/DistrContact'
  171. import SelectUser from '@/components/SelectUser'
  172. export default {
  173. name: 'omsIndex',
  174. components: {
  175. CustomerContact,
  176. ProjectContact,
  177. DistrContact,
  178. SelectUser,
  179. },
  180. data() {
  181. return {
  182. flag: true,
  183. imageValue: [],
  184. height: '',
  185. paddingTop: '',
  186. showMode: false, //选择行业
  187. showDate: false,
  188. maxDate: new Date() * 1,
  189. modeColumns: [
  190. [
  191. {
  192. label: '电话',
  193. id: '10',
  194. },
  195. {
  196. label: '邮件',
  197. id: '20',
  198. },
  199. {
  200. label: '拜访',
  201. id: '30',
  202. },
  203. ],
  204. ],
  205. addForm: {
  206. files: [],
  207. date: this.parseTime(new Date(), '{y}-{m}-{d}'), //时间
  208. content: '', //客户姓名
  209. furtherPlan: '', //下一步跟进计划
  210. effect: '',
  211. issue: '',
  212. mode: '', //跟进方式
  213. modeId: 0,
  214. fileName: '', //附件
  215. contactsName: '', //联系人
  216. contactsId: 0, //联系人ID
  217. },
  218. targetType: 0, //跟进对象类型(10客户,20项目,30合同,40回款,50经销商,50代理商)
  219. rules: {
  220. furtherPlan: {
  221. type: 'string',
  222. required: true,
  223. message: '请填写下一步跟进计划和目标',
  224. trigger: ['blur'],
  225. },
  226. content: {
  227. type: 'string',
  228. required: true,
  229. message: '请填写跟进内容',
  230. trigger: ['blur'],
  231. },
  232. effect: {
  233. type: 'string',
  234. required: true,
  235. message: '请填写达成效果',
  236. trigger: ['blur'],
  237. },
  238. issue: {
  239. type: 'string',
  240. required: true,
  241. message: '请填写问题或困难',
  242. trigger: ['blur'],
  243. },
  244. date: {
  245. type: 'string',
  246. required: true,
  247. message: '请填写跟进时间',
  248. trigger: ['blur'],
  249. },
  250. mode: {
  251. type: 'string',
  252. required: true,
  253. message: '请选择跟进方式',
  254. trigger: ['blur', 'change'],
  255. },
  256. contactsName: {
  257. type: 'string',
  258. required: true,
  259. message: '请选择联系人',
  260. trigger: ['blur', 'change'],
  261. },
  262. },
  263. details: {},
  264. curId: 0,
  265. }
  266. },
  267. computed: {
  268. // ...mapGetters(['details']),
  269. },
  270. onLoad(option) {
  271. this.targetType = option.targetType
  272. this.curId = parseInt(option.id)
  273. },
  274. created() {
  275. const navData = uni.getMenuButtonBoundingClientRect()
  276. this.height = navData.height + 'px'
  277. this.paddingTop = navData.top + 'px'
  278. },
  279. mounted() {
  280. this.initData()
  281. },
  282. methods: {
  283. // 获取客户/项目信息
  284. async initData() {
  285. if (this.targetType == '10') {
  286. //客户
  287. const [err, res] = await to(
  288. custApi.getDetail({
  289. ids: [this.curId],
  290. })
  291. )
  292. if (err) return
  293. if (res && res.code == 200) {
  294. this.details = res.data.list[0]
  295. }
  296. } else if (this.targetType == '20') {
  297. //项目
  298. const [err, res] = await to(
  299. prodApi.getDetail({
  300. id: this.curId,
  301. })
  302. )
  303. if (err) return
  304. if (res && res.code == 200) {
  305. this.details = res.data
  306. }
  307. } else if (this.targetType == '50') {
  308. // 经销商代理商
  309. const [err, res] = await to(distrApi.getEntity({ id: this.curId }))
  310. if (err) return
  311. if (res && res.code == 200) {
  312. this.details = res.data.list
  313. }
  314. } else if (this.targetType == '60') {
  315. // 招标信息
  316. const [err, res] = await to(bidApi.get({ id: this.curId }))
  317. if (err) return
  318. if (res && res.code == 200) {
  319. this.details = res.data
  320. }
  321. } else if (this.targetType == '70') {
  322. // 合作伙伴信息
  323. const [err, res] = await to(partnerApi.getCompanyConcat({ id: this.curId }))
  324. if (err) return
  325. if (res && res.code == 200) {
  326. this.details = res.data
  327. }
  328. }
  329. },
  330. // 打开联系人
  331. open() {
  332. if (this.targetType == '10') {
  333. this.$refs.concat.open(this.details.id)
  334. } else if (this.targetType == '20') {
  335. this.$refs.projectConcat.open(this.details.id, this.details.custId)
  336. } else if (this.targetType == '50') {
  337. this.$refs.distrConcat.open(this.details.id)
  338. } else if (this.targetType == '60') {
  339. this.$refs.concat.open(this.details.custId)
  340. } else if (this.targetType == '70') {
  341. this.$refs.userConcat.open()
  342. }
  343. },
  344. // 行业选择
  345. pickMode(e) {
  346. this.addForm.modeId = e.value[0].id
  347. this.addForm.mode = e.value[0].label
  348. this.showMode = false
  349. },
  350. async pickDate(e) {
  351. this.showDate = false
  352. const timeFormat = uni.$u.timeFormat
  353. let timeValue = await timeFormat(e.value, 'yyyy-mm-dd hh:MM')
  354. this.addForm.date = timeValue
  355. },
  356. // 关闭选择用户
  357. closeConcat(user) {
  358. if (user) {
  359. if (this.targetType == '50') {
  360. this.addForm.contactsName = user.name
  361. this.addForm.contactsId = user.id
  362. } else {
  363. this.addForm.contactsName = user.label
  364. this.addForm.contactsId = user.id
  365. }
  366. }
  367. },
  368. handleAdd() {
  369. if (!this.flag) return
  370. this.$refs.addForm
  371. .validate()
  372. .then(async () => {
  373. let idParams = {}
  374. if (this.targetType == '10') {
  375. idParams = {
  376. custId: this.details.id,
  377. custName: this.details.custName,
  378. targetId: this.details.id,
  379. targetName: this.details.custName,
  380. }
  381. } else if (this.targetType == '20') {
  382. idParams = {
  383. custId: this.details.custId,
  384. custName: this.details.custName,
  385. targetId: this.details.id,
  386. targetName: this.details.nboName,
  387. }
  388. } else if (this.targetType == '50') {
  389. idParams = {
  390. targetId: this.details.id,
  391. targetName: this.details.distName,
  392. }
  393. } else if (this.targetType == '60') {
  394. idParams = {
  395. targetId: this.details.id,
  396. targetName: this.details.title,
  397. }
  398. } else if (this.targetType == '70') {
  399. idParams = {
  400. custId: null,
  401. custName: null,
  402. targetId: this.details.id,
  403. targetName: this.details.name,
  404. }
  405. }
  406. let params = Object.assign(
  407. {
  408. contactsId: this.addForm.contactsId,
  409. contactsName: this.addForm.contactsName,
  410. followContent: this.addForm.content,
  411. effect: this.addForm.effect,
  412. issue: this.addForm.issue,
  413. followDate: this.addForm.date,
  414. followType: this.addForm.modeId,
  415. targetType: this.targetType,
  416. furtherPlan: this.addForm.furtherPlan,
  417. files: this.addForm.files,
  418. },
  419. idParams
  420. )
  421. this.flag = false
  422. const [err, res] = await to(followApi.createFollow(params))
  423. this.flag = true
  424. if (err) return
  425. if (res && res.code == 200) {
  426. this.$refs.uToast.show({
  427. type: 'success',
  428. message: '创建成功',
  429. complete: () => {
  430. uni.navigateBack({
  431. //关闭当前页面,返回上一页面或多级页面。
  432. delta: 1,
  433. })
  434. },
  435. })
  436. }
  437. })
  438. .catch((err) => {
  439. this.$refs.uNotify.show({
  440. top: this.height + this.paddingTop + 10,
  441. type: 'warning',
  442. message: err[0].message,
  443. duration: 1000 * 3,
  444. })
  445. })
  446. },
  447. // // 选择上传触发函数
  448. select(e) {
  449. let url = process.uniEnv.VUE_APP_UPLOAD_WEED
  450. uni.request({
  451. url, //仅为示例,并非真实接口地址。
  452. success: (res) => {
  453. this.uploadFiles(e, res.data)
  454. },
  455. })
  456. },
  457. // 上传函数
  458. async uploadFiles(e, res) {
  459. let action = process.uniEnv.VUE_APP_PROTOCOL + res.publicUrl + '/' + res.fid
  460. uni.uploadFile({
  461. url: action,
  462. filePath: e.tempFilePaths[0],
  463. name: 'file',
  464. formData: {
  465. file: e.tempFiles[0].file,
  466. },
  467. success: (res) => {
  468. this.$refs.uToast.show({
  469. type: 'success',
  470. message: '附件上传成功',
  471. })
  472. this.addForm.files = [
  473. {
  474. fileName: e.tempFiles[0].file.name,
  475. fileUrl: action,
  476. },
  477. ]
  478. },
  479. fail: (err) => {},
  480. })
  481. },
  482. goBack() {
  483. uni.navigateBack({
  484. //关闭当前页面,返回上一页面或多级页面。
  485. delta: 1,
  486. })
  487. },
  488. },
  489. }
  490. </script>
  491. <style>
  492. page {
  493. background: #f2f3f5;
  494. }
  495. </style>
  496. <style lang="scss" scoped>
  497. .home {
  498. padding-top: 188rpx;
  499. .nav {
  500. position: absolute;
  501. left: 0;
  502. top: 0;
  503. width: 100%;
  504. height: 284rpx;
  505. background: #3e7ef8;
  506. .title {
  507. position: relative;
  508. text-align: center;
  509. font-size: 32rpx;
  510. font-weight: bold;
  511. color: #ffffff;
  512. .back {
  513. position: absolute;
  514. top: 0;
  515. bottom: 0;
  516. margin: auto;
  517. left: 70rpx;
  518. display: flex;
  519. }
  520. }
  521. }
  522. .main {
  523. position: absolute;
  524. width: 100%;
  525. height: calc(100vh - 188rpx);
  526. background: #ffffff;
  527. box-shadow: 0 6rpx 19rpx 2rpx rgba(0, 45, 132, 0.15);
  528. border-radius: 31rpx 31rpx 0 0;
  529. padding: 0 32rpx;
  530. overflow: auto;
  531. padding-bottom: 64rpx;
  532. .form-label {
  533. font-size: 32rpx;
  534. font-weight: bold;
  535. color: #323232;
  536. padding-bottom: 18rpx;
  537. .label-tag {
  538. width: 15rpx;
  539. height: 15rpx;
  540. background: #ff4d4f;
  541. border-radius: 50%;
  542. margin-right: 10rpx;
  543. }
  544. }
  545. .upload-file-box {
  546. padding: 20rpx 0;
  547. position: relative;
  548. .upload-btn {
  549. position: absolute;
  550. right: 0;
  551. top: 26rpx;
  552. }
  553. }
  554. }
  555. .save {
  556. width: 569rpx;
  557. height: 92rpx;
  558. background: #3e7ef8;
  559. border-radius: 31rpx;
  560. margin: 116rpx auto 0;
  561. font-size: 32rpx;
  562. color: #ffffff;
  563. text-align: center;
  564. line-height: 92rpx;
  565. }
  566. }
  567. </style>