GiftSubmitDialog.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. <template>
  2. <CustomDialog
  3. width="600px"
  4. top="18vh"
  5. :title="title"
  6. class="gift-submit-dialog"
  7. :visible="visible"
  8. >
  9. <div class="gift-submit-header">
  10. <div class="gift-submit-header__item">
  11. <span>订阅区域:</span>
  12. <span>{{ subduration.areacount }}个省</span>
  13. </div>
  14. <div class="gift-submit-header__item">
  15. <span>可赠送时长(取整):</span>
  16. <span>{{ subduration.gifted }}个月</span>
  17. </div>
  18. </div>
  19. <div class="gift-submit-body">
  20. <div class="gift-person-list-button">
  21. <span>人员列表</span>
  22. <span class="gift-person-list-button__icon" @click="addPerson">+</span>
  23. </div>
  24. <div class="gift-person-tip">
  25. 说明:如手机号尚未注册剑鱼,赠送其超级订阅后,平台会自动帮其按照对应手机号注册。
  26. </div>
  27. <div class="gift-person-list">
  28. <div v-for="(item, index) in personList" :key="index" class="gift-person-list__item">
  29. <el-form :ref="`form${index}`" :model="item" :rules="rules" class="gift-person-info-wrapper">
  30. <el-form-item label="朋友手机号" prop="phone" class="gift-person-info">
  31. <el-input
  32. v-model="item.phone"
  33. maxlength="11"
  34. class="custom-long-input"
  35. placeholder="请输入手机号"
  36. @blur="validateSingleForm(index, item, 'phone')"
  37. />
  38. </el-form-item>
  39. <el-form-item label="赠予时长" prop="monthnum" class="gift-person-info time">
  40. <el-input
  41. v-model="item.monthnum"
  42. type="number"
  43. class="custom-long-input"
  44. placeholder="请输入整数"
  45. @blur="validateSingleForm(index, item, 'monthnum')"
  46. />
  47. <span class="unit">个月</span>
  48. </el-form-item>
  49. </el-form>
  50. <div v-show="index !== 0" class="delete-person" @click="deletePerson(index)">
  51. <span class="icon-delete-button" />
  52. </div>
  53. <div v-if="item.status === -1" class="phone-no-register-tip">
  54. {{ statusMessages[item.status] }}
  55. </div>
  56. <div v-else class="info-error-tip">
  57. {{ statusMessages[item.status] }}
  58. </div>
  59. </div>
  60. </div>
  61. <div v-if="monthNumTotal > 0" class="gift-total-tip">
  62. 共赠送<span> {{ personList.length }} </span>人,赠送时长<span> {{ monthNumTotal }} </span>个月,剩余<span> {{ getGifted }} </span>个月可赠送
  63. </div>
  64. <div class="gift-read-agree">
  65. <el-checkbox v-model="checked">
  66. 阅读并同意<a href="javascript:;">《“送好友超级订阅”产品须知》</a>
  67. </el-checkbox>
  68. </div>
  69. </div>
  70. <span slot="footer" class="dialog-footer-wrapper">
  71. <button
  72. class="action-button confirm"
  73. :disabled="!isFormValid"
  74. @click="onClickConfirm"
  75. >
  76. 提交
  77. </button>
  78. <button class="action-button cancel" @click="$emit('close')">
  79. 取消
  80. </button>
  81. </span>
  82. </CustomDialog>
  83. </template>
  84. <script>
  85. import CustomDialog from '@/components/dialog/Dialog.vue'
  86. import { getInfoByPhone, getSubDuration, setTransferSubDuration } from '@/api/modules/'
  87. export default {
  88. name: 'GiftSubmitDialog',
  89. components: {
  90. CustomDialog
  91. },
  92. props: {
  93. visible: Boolean,
  94. title: {
  95. type: String,
  96. default: '送给朋友'
  97. },
  98. },
  99. data() {
  100. const validatePhone = (rule, value, callback) => {
  101. if (value === '') {
  102. callback(new Error('请输入手机号'))
  103. }
  104. else if (!/^1[3-9]\d{9}$/.test(value)) {
  105. callback(new Error('手机号码格式不正确'))
  106. }
  107. else {
  108. callback()
  109. }
  110. }
  111. const validateMonthnum = (rule, value, callback) => {
  112. if (value === '') {
  113. callback(new Error('请输入赠予时长'))
  114. }
  115. else if (value <= 0) {
  116. callback(new Error('赠予时长应大于0'))
  117. }
  118. else {
  119. callback()
  120. }
  121. }
  122. return {
  123. checked: false,
  124. personList: [
  125. {
  126. phone: '',
  127. monthnum: '',
  128. status: '',
  129. error: '',
  130. phoneValid: false,
  131. monthnumValid: false
  132. }
  133. ],
  134. rules: {
  135. phone: [
  136. { validator: validatePhone, trigger: 'blur' }
  137. ],
  138. monthnum: [
  139. { validator: validateMonthnum, trigger: 'blur' },
  140. ]
  141. },
  142. statusMessages: {
  143. '1': '',
  144. '-1': '提示:手机号尚未注册剑鱼,赠送其超级订阅后,平台会自动帮其按照对应手机号注册。',
  145. '-2': '手机号已是超级订阅会员,且购买省份与当前省份不一致,不可赠送。',
  146. '-3': '不能将超级订阅赠送给自己,请更换手机号。该提示展示在对应手机号下方'
  147. },
  148. subduration: {}
  149. }
  150. },
  151. computed: {
  152. isFormValid() {
  153. return this.personList.every(item => item.phoneValid && item.monthnumValid)
  154. },
  155. monthNumTotal() {
  156. return this.personList.reduce((total, item) => {
  157. return total + Number(item.monthnum) || 0
  158. }, 0)
  159. },
  160. // 剩余可赠送时长
  161. getGifted() {
  162. return Number(this.subduration.gifted) - this.monthNumTotal || 0
  163. }
  164. },
  165. created() {
  166. this.getSubDurationEvent()
  167. },
  168. methods: {
  169. async getSubDurationEvent() {
  170. const { error_code: code, data } = await getSubDuration()
  171. if (code === 0) {
  172. this.subduration = data
  173. }
  174. },
  175. /**
  176. * 验证单个表单
  177. *
  178. * @param {number} index - 表单的索引
  179. */
  180. validateSingleForm(index, item, fieldname) {
  181. if (this.$refs[`form${index}`]) {
  182. this.$refs[`form${index}`][0].validateField(fieldname, (error) => {
  183. if (fieldname === 'phone') {
  184. if (!error) {
  185. // 需要只调用一次接口,避免多次请求
  186. if (item.status !== 1) {
  187. this.getInfoByPhoneEvent(index, item)
  188. }
  189. }
  190. else {
  191. item.phoneValid = false
  192. }
  193. }
  194. else if (fieldname === 'monthnum') {
  195. item.monthnumValid = !error
  196. }
  197. })
  198. }
  199. },
  200. async getInfoByPhoneEvent(index, item) {
  201. // 此处可以调用接口验证手机号是否已经注册剑鱼
  202. try {
  203. const { error_code: code, data } = await getInfoByPhone({
  204. phone: item.phone
  205. })
  206. if (code === 0) {
  207. item.status = data.status
  208. item.error = this.statusMessages[data.status]
  209. if (data.status === 1 || data.status === -1) {
  210. item.phoneValid = true
  211. }
  212. else {
  213. item.phoneValid = false
  214. }
  215. // 当data.status不等于1时,移除同组的赠予时长输入框校验结果
  216. // if (data.status !== '1') {
  217. // this.$refs[`form${index}`][0].clearValidate('monthnum')
  218. // }
  219. // 当data.status不等于1时,检查personList中的每一项的status,如果有任何一个的status不等于1,提交按钮不能点击
  220. // for (let i = 0; i < this.personList.length; i++) {
  221. // if (this.personList[i].status !== 1 && this.personList[i].status !== -1) {
  222. // this.isFormValid = false
  223. // return
  224. // }
  225. // }
  226. }
  227. }
  228. catch (error) {
  229. console.log(error)
  230. item.phoneValid = true
  231. }
  232. },
  233. /**
  234. * 更新整体表单的有效性
  235. */
  236. updateFormValidity() {
  237. const validationPromises = this.personList.map((item, index) => {
  238. return new Promise((resolve) => {
  239. try {
  240. this.$refs[`form${index}`][0].validate((valid) => {
  241. resolve(valid)
  242. })
  243. }
  244. catch (e) {
  245. resolve(false)
  246. }
  247. })
  248. })
  249. Promise.all(validationPromises).then((results) => {
  250. this.isFormValid = results.every(result => result)
  251. })
  252. },
  253. addPerson() {
  254. this.personList.push({
  255. phone: '',
  256. monthnum: '',
  257. status: '',
  258. error: '',
  259. phoneValid: false,
  260. monthnumValid: false
  261. })
  262. this.updateFormValidity()
  263. },
  264. /**
  265. * 重置人员列表
  266. */
  267. resetPersonList() {
  268. this.personList = [
  269. {
  270. phone: '',
  271. monthnum: '',
  272. status: '',
  273. error: '',
  274. phoneValid: false,
  275. monthnumValid: false
  276. }
  277. ]
  278. },
  279. /**
  280. * 删除指定索引位置的人员
  281. *
  282. * @method deletePerson
  283. */
  284. deletePerson(index) {
  285. this.personList.splice(index, 1)
  286. this.updateFormValidity()
  287. },
  288. onClickConfirm() {
  289. if (!this.checked)
  290. return this.$toast('请勾选协议')
  291. if (this.isFormValid) {
  292. this.confirmGiftData()
  293. }
  294. },
  295. async confirmGiftData() {
  296. // 参数格式:{phones:{18439509554: 1,18439509555: 2}}
  297. const data = this.personList.reduce((acc, cur) => {
  298. if (cur.phone && cur.monthnum) {
  299. acc[cur.phone] = cur.monthnum
  300. }
  301. return acc
  302. }, {})
  303. const { error_code: code, error_msg: msg } = await setTransferSubDuration({ phones: JSON.stringify(data) })
  304. if (code === 0) {
  305. this.$toast('赠送成功')
  306. }
  307. else {
  308. this.$toast(msg)
  309. }
  310. this.$emit('close')
  311. this.resetPersonList()
  312. }
  313. }
  314. }
  315. </script>
  316. <style lang="scss" scoped>
  317. .gift-submit-dialog {
  318. ::v-deep {
  319. .el-dialog__header {
  320. text-align: left;
  321. .el-dialog__title {
  322. padding-left: 10px;
  323. line-height: 28px;
  324. }
  325. &::after {
  326. content: '';
  327. position: absolute;
  328. left: 20px;
  329. top: 26px;
  330. display: inline-block;
  331. width: 2px;
  332. height: 16px;
  333. background: #2ABED1;
  334. }
  335. }
  336. .el-dialog__body {
  337. padding: 0 20px 10px;
  338. .gift-submit-header {
  339. display: flex;
  340. align-items: center;
  341. padding-bottom: 10px;
  342. border-bottom: 1px solid #2ABED1;
  343. &__item {
  344. margin-right: 24px;
  345. }
  346. }
  347. .gift-submit-body {
  348. padding: 10px 0;
  349. .gift-person-list-button {
  350. display: flex;
  351. align-items: center;
  352. font-size: 16px;
  353. line-height: 24px;
  354. color: #1D1D1D;
  355. }
  356. .gift-person-list-button__icon {
  357. display: flex;
  358. justify-content: center;
  359. margin-left: 10px;
  360. width: 20px;
  361. height: 20px;
  362. line-height: 18px;
  363. border-radius: 50%;
  364. background-color: #2ABED1;
  365. color: #fff;
  366. font-size: 18px;
  367. cursor: pointer;
  368. }
  369. .gift-person-tip {
  370. margin: 10px 0;
  371. font-size: 14px;
  372. line-height: 22px;
  373. color: #2ABED1;
  374. }
  375. .gift-person-list {
  376. max-height: 200px;
  377. padding: 10px;
  378. border: 1px solid #ECECEC;
  379. border-radius: 8px;
  380. overflow-y: auto;
  381. .gift-person-list__item {
  382. position: relative;
  383. display: flex;
  384. flex-direction: column;
  385. justify-content: center;
  386. margin-bottom: 10px;
  387. padding: 10px 20px;
  388. width: 480px;
  389. border-radius: 8px;
  390. background: linear-gradient(to bottom, #F6F6F6, #fff);
  391. .gift-person-info-wrapper {
  392. display: flex;
  393. align-items: center;
  394. .el-form-item {
  395. margin-bottom: 0;
  396. }
  397. .el-form-item__label {
  398. font-size: 14px;
  399. line-height: 22px;
  400. color: #1D1D1D;
  401. &::before {
  402. content: '';
  403. }
  404. }
  405. }
  406. .delete-person {
  407. position: absolute;
  408. right: -30px;
  409. top: 0;
  410. .icon-delete-button {
  411. display: inline-block;
  412. width: 20px;
  413. height: 20px;
  414. background: url('~@/assets/images/icon-delete.png') no-repeat;
  415. background-size:20px 20px ;
  416. cursor: pointer;
  417. }
  418. }
  419. }
  420. .gift-person-info {
  421. font-size: 14px;
  422. color: #1D1D1D;
  423. &.time {
  424. position: relative;
  425. margin-left: 24px;
  426. width: 120px;
  427. .unit {
  428. position: absolute;
  429. top: 30px;
  430. right: -40px;
  431. }
  432. }
  433. }
  434. .custom-long-input {
  435. margin-top: 8px;
  436. height: 36px;
  437. .el-input__inner {
  438. height: 36px;
  439. line-height: 36px;
  440. }
  441. }
  442. .phone-no-register-tip, .info-error-tip {
  443. margin-top: 10px;
  444. font-size: 14px;
  445. line-height: 22px;
  446. color: #2ABED1;
  447. }
  448. .info-error-tip {
  449. color: #FF3A20;
  450. }
  451. }
  452. .gift-total-tip {
  453. margin-top: 10px;
  454. font-size: 14px;
  455. line-height: 22px;
  456. color: #686868;
  457. span {
  458. color: #2ABED1;
  459. }
  460. }
  461. .gift-read-agree {
  462. margin-top: 10px;
  463. font-size: 14px;
  464. color: #686868;
  465. .el-checkbox.is-checked {
  466. .el-checkbox__label {
  467. color: #888888;
  468. }
  469. }
  470. a {
  471. color: #2ABED1;
  472. text-decoration: none;
  473. }
  474. }
  475. }
  476. }
  477. .dialog-footer {
  478. justify-content: center;
  479. }
  480. }
  481. .dialog-footer-wrapper {
  482. display: flex;
  483. justify-content: center;
  484. align-items: center;
  485. button {
  486. width: 132px;
  487. height: 36px;
  488. background: #2ABED1;
  489. color: #fff;
  490. font-size: 16px;
  491. border: none;
  492. &.cancel {
  493. background: #fff;
  494. color: #1D1D1D;
  495. border: 1px solid #E0E0E0;
  496. }
  497. }
  498. }
  499. }
  500. </style>