publishNews.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. <template>
  2. <view class="publishMain" :style="{ paddingTop: barHeight + 'px' }">
  3. <!-- <uni-popup ref="addressPopup" background-color="#fff">
  4. <view class="popup-content">
  5. <LocalSearch
  6. @changeAddress="changeAddress"
  7. @closeSelectAddress="closeSelectAddress"
  8. ref="localRef"
  9. ></LocalSearch>
  10. </view>
  11. </uni-popup> -->
  12. <page-container :show='pageContainerShow' :overlay="pageContainerOverlay" position='right' @beforeleave='afterenter' ref="addressPopup" background-color="#fff">
  13. <view class="popup-content">
  14. <LocalSearch
  15. @changeAddress="changeAddress"
  16. @closeSelectAddress="closeSelectAddress"
  17. ref="localRef"
  18. ></LocalSearch>
  19. </view>
  20. </page-container>
  21. <view class="content">
  22. <uni-easyinput type="textarea" v-model="formDatas.title" placeholder="来点内容吧..." />
  23. <view class="fileBox">
  24. <view class="fileItem" v-for="(item, index) in fileList" :key="index">
  25. <image
  26. src="../../static/image/delete.png"
  27. @click="deleteFiles(index)"
  28. class="deleteFile"
  29. ></image>
  30. <image
  31. :src="item.materialUrl"
  32. class="thumbnail"
  33. v-if="item.fileType === 'image'"
  34. @click="previewFile(index)"
  35. mode="aspectFill"
  36. ></image>
  37. <view
  38. class="defaultFileItem"
  39. v-else-if="item.fileType === 'video'"
  40. @click="previewFile(index)"
  41. >
  42. <image src="../../static/image/video.png" class="thumbnail"></image>
  43. <image src="../../static/image/playBtn.png" class="playBtn"></image>
  44. </view>
  45. <view class="defaultFileItem" v-else @click="previewFile(index)">
  46. <image src="../../static/image/music.png" class="thumbnail"></image>
  47. <image src="../../static/image/playBtn.png" class="playBtn"></image>
  48. </view>
  49. </view>
  50. <view class="fileItem addFileBtn" v-if="fileList.length < 9" @click="addFile"></view>
  51. </view>
  52. <view class="formBox">
  53. <view class="phoneBox">
  54. <view class="label">
  55. 手机号
  56. </view>
  57. <view class="textRight">
  58. {{ phone }}
  59. </view>
  60. </view>
  61. <uni-forms id="publishForm" ref="valiForm" :rules="rules" :modelValue="formDatas">
  62. <uni-forms-item label="地点" required name="tipAddress">
  63. <view class="selectInputBox" @click="openSelectAddress">
  64. <text :class="formDatas.tipAddress == '' ? 'addressNoText' : ''">
  65. {{ formatAddress(formDatas.tipAddress) || '请选择' }}
  66. </text>
  67. <text class="rightIcon">{{ '>' }}</text>
  68. </view>
  69. </uni-forms-item>
  70. <uni-forms-item label="昵称" required name="tipUsername">
  71. <uni-easyinput type="nickname" v-model="formDatas.tipUsername" placeholder="请输入" class="textRight" />
  72. </uni-forms-item>
  73. <!-- <uni-forms-item label="发布部门" required name="ascriptionName">
  74. <view class="selectInputBox" @click="openAscriptionAction">
  75. <text>{{ formDatas.ascriptionName }}</text>
  76. <text class="rightIcon">{{ '>' }}</text>
  77. </view>
  78. </uni-forms-item> -->
  79. </uni-forms>
  80. </view>
  81. </view>
  82. <view class="publishBtn" @click="publishNews()">发布</view>
  83. <view class="popupBottom" v-if="isshowAudio" @touchmove.stop.prevent="moveStop">
  84. <view class="popupBg" @click="isshowAudio = false"></view>
  85. <view class="popupContent">
  86. <sound-recording
  87. :maximum="15"
  88. :duration="100"
  89. @cancel="isshowAudio = false"
  90. @confirm="uploadFile"
  91. ></sound-recording>
  92. </view>
  93. </view>
  94. <view class="popupBottom" v-if="previewModal" @touchmove.stop.prevent="moveStop">
  95. <view class="popupSwiperBg" @click="previewModal = false"></view>
  96. <view class="swiperDotBox">
  97. <swiper class="swiperBox" @change="swiperChange" :current="previewIndex - 1">
  98. <swiper-item v-for="(item, index) in fileList" :key="index">
  99. <image
  100. :src="item.materialUrl"
  101. class="swiperFileItem"
  102. v-if="item.fileType === 'image'"
  103. mode="aspectFit"
  104. @click="previewModal = false"
  105. ></image>
  106. <video
  107. :src="item.materialUrl"
  108. class="swiperFileItem"
  109. :show-fullscreen-btn="false"
  110. controls
  111. ></video>
  112. <video
  113. </swiper-item>
  114. </swiper>
  115. <view class="swiperPage">{{ `${previewIndex} / ${fileList.length}` }}</view>
  116. </view>
  117. </view>
  118. </view>
  119. </template>
  120. <script>
  121. import { saveClue, uploadMediaFile } from '../../network/api.js';
  122. import { LocalSearch } from '../../components/localSearch/localSearch.vue';
  123. import { mapState, mapMutations } from 'vuex';
  124. export default {
  125. components: {
  126. LocalSearch
  127. },
  128. computed: {
  129. ...mapState(['userInfo'])
  130. },
  131. data() {
  132. return {
  133. phone: '',
  134. previewIndex: 0,
  135. previewModal: false,
  136. isSubmitForm: true,
  137. isshowAudio: false,
  138. fileList: [],
  139. formDatas: {
  140. title: '', //标题
  141. tipAddress: '', //地址
  142. tipUsername: '', //昵称
  143. // ascription: 1,
  144. // ascriptionName: '都市快报'
  145. },
  146. barHeight: uni.getSystemInfoSync().statusBarHeight,
  147. rules: {
  148. tipAddress: {
  149. rules: [
  150. {
  151. required: true,
  152. errorMessage: '请选择地点'
  153. }
  154. ]
  155. },
  156. tipUsername: {
  157. rules: [
  158. {
  159. required: true,
  160. errorMessage: '昵称不能为空'
  161. }
  162. ]
  163. }
  164. },
  165. pageContainerShow:false,
  166. pageContainerOverlay:false
  167. };
  168. },
  169. mounted() {
  170. this.phone = this.userInfo.phone
  171. this.formDatas.tipUsername = this.userInfo.name
  172. this.$refs.valiForm.setValue('tipUsername', this.userInfo.name)
  173. },
  174. methods: {
  175. ...mapMutations(['setUserInfo']),
  176. deleteFiles(index) {
  177. this.fileList.splice(index, 1);
  178. },
  179. swiperChange(e) {
  180. this.previewIndex = e.detail.current + 1;
  181. },
  182. previewFile(index) {
  183. this.previewIndex = index + 1;
  184. this.previewModal = true;
  185. },
  186. moveStop() {},
  187. openAscriptionAction() {
  188. let action = ['都市快报', '第一新闻热线', '管理中心'];
  189. uni.showActionSheet({
  190. itemList: action,
  191. success: res => {
  192. this.formDatas.ascription = res.tapIndex + 1;
  193. this.formDatas.ascriptionName = action[res.tapIndex];
  194. }
  195. });
  196. },
  197. addFile() {
  198. let action = ['图片', '视频', '音频'];
  199. uni.showActionSheet({
  200. itemList: action,
  201. success: res => {
  202. let actionMethods = ['photo', 'video', 'audio'];
  203. this[actionMethods[res.tapIndex]]();
  204. }
  205. });
  206. },
  207. photo() {
  208. uni.chooseImage({
  209. success: res => {
  210. console.log(res);
  211. this.uploadFiles(res.tempFilePaths, 'image');
  212. }
  213. });
  214. },
  215. video() {
  216. uni.chooseMedia({
  217. count: 9,
  218. mediaType: ['image', 'video'],
  219. maxDuration: 60,
  220. camera: 'back',
  221. success: res => {
  222. console.log(res);
  223. this.uploadFiles(res.tempFiles, 'video');
  224. }
  225. });
  226. },
  227. videoCompress(tempFilePath) {
  228. return new Promise((resolve, reject) => {
  229. uni.compressVideo({
  230. src: tempFilePath,
  231. quality: 'medium', //'low':低,'medium':中,'high':高
  232. success: res => {
  233. console.log('压缩后', res);
  234. resolve(res.tempFilePath);
  235. },
  236. fail: err => {
  237. reject(err);
  238. }
  239. });
  240. });
  241. },
  242. async uploadFiles(file, type) {
  243. console.log(file);
  244. for (let i = 0; i < file.length; i++) {
  245. if (this.fileList.length >= 9) {
  246. uni.showToast({
  247. title: '已上传九个素材,无法再上传!',
  248. duration: 1000,
  249. icon: 'none'
  250. });
  251. break;
  252. }
  253. let path = file[i];
  254. if (type == 'video') {
  255. path = await this.videoCompress(path.tempFilePath).catch(err => {
  256. console.log(err);
  257. uni.showToast({
  258. title: err,
  259. duration: 1000
  260. });
  261. });
  262. }
  263. if (!path) continue;
  264. let mediaType = {
  265. image: {
  266. type: 1,
  267. fileType: 'image'
  268. },
  269. video: {
  270. type: 2,
  271. fileType: 'video'
  272. },
  273. audio: {
  274. type: 3,
  275. fileType: 'voice'
  276. },
  277. };
  278. let params = {
  279. filePath: path,
  280. formData: {
  281. mediaType: mediaType[type].fileType
  282. },
  283. fileType: type
  284. };
  285. let result = await uploadMediaFile(params);
  286. if (result.state === 200) {
  287. this.fileList.push({
  288. materialUrl: result.data,
  289. fileType: type,
  290. type: mediaType[type].type
  291. });
  292. } else {
  293. uni.showToast({
  294. title: '上传失败!',
  295. duration: 1000,
  296. icon: 'error'
  297. });
  298. }
  299. }
  300. },
  301. audio() {
  302. this.isshowAudio = true;
  303. },
  304. uploadFile(tempFilePath) {
  305. this.uploadFiles([tempFilePath], 'audio');
  306. this.isshowAudio = false;
  307. },
  308. afterenter(){
  309. this.pageContainerShow = false
  310. this.pageContainerOverlay = false
  311. },
  312. openSelectAddress() {
  313. // this.$refs.addressPopup.open('bottom');
  314. this.pageContainerShow = true
  315. this.pageContainerOverlay = true
  316. },
  317. closeSelectAddress() {
  318. // this.$refs.addressPopup.close();
  319. this.pageContainerShow = false
  320. this.pageContainerOverlay = false
  321. },
  322. changeAddress(text) {
  323. this.formDatas.tipAddress = text;
  324. this.$refs.valiForm.setValue('tipAddress', text);
  325. this.closeSelectAddress();
  326. },
  327. back() {
  328. uni.navigateBack();
  329. },
  330. publishNews() {
  331. if (!this.isSubmitForm) return;
  332. this.$refs.valiForm
  333. .validate(async (err, formData) => {
  334. if (!err) {
  335. console.log('success', formData);
  336. this.formDatas.title = this.formDatas.title.trim();
  337. if (this.formDatas.title.length === 0) {
  338. uni.showToast({
  339. title: '内容不能为空',
  340. duration: 2000,
  341. icon: 'none'
  342. });
  343. return;
  344. }
  345. this.isSubmitForm = false;
  346. let params = {
  347. ascription: 1,
  348. source: 4,
  349. title: this.formDatas.title,
  350. phone: this.userInfo.phone,
  351. address: formData.tipAddress,
  352. author: formData.tipUsername,
  353. clueInfoList: this.fileList
  354. };
  355. let res = await saveClue(params);
  356. if (res.data.state === 200) {
  357. uni.showToast({
  358. duration: 2000,
  359. title: '发布成功!',
  360. icon: 'success'
  361. });
  362. let userInfos = Object.assign({}, this.userInfo)
  363. userInfos.name = formData.tipUsername
  364. this.setUserInfo(userInfos)
  365. uni.setStorageSync('userInfo', JSON.stringify(userInfos))
  366. this.formDatas = this.$options.data().formDatas;
  367. this.fileList = [];
  368. this.$refs.localRef.initIndex()
  369. this.$refs.valiForm.resetFields();
  370. this.formDatas.tipUsername = this.userInfo.name
  371. this.$refs.valiForm.setValue('tipUsername', this.userInfo.name)
  372. } else {
  373. uni.showToast({
  374. duration: 2000,
  375. title: res.data.message,
  376. icon: 'none'
  377. });
  378. }
  379. this.isSubmitForm = true;
  380. }
  381. })
  382. .then(res => {
  383. // res 返回 null
  384. uni.showToast({
  385. title: res,
  386. icon: 'none'
  387. })
  388. });
  389. },
  390. formatAddress(item) {
  391. return item.length > 8 ? item.substring(0, 7) + '...' : item;
  392. }
  393. }
  394. };
  395. </script>
  396. <style lang="scss" scoped>
  397. .publishMain {
  398. box-sizing: border-box;
  399. padding: 0 32rpx 32rpx 32rpx;
  400. width: 100%;
  401. .header {
  402. display: flex;
  403. justify-content: space-between;
  404. align-items: center;
  405. height: 49px;
  406. }
  407. .content {
  408. .fileBox {
  409. display: flex;
  410. flex-wrap: wrap;
  411. margin-top: 10px;
  412. .fileItem {
  413. width: 218rpx;
  414. height: 218rpx;
  415. background-color: #a5a5a5;
  416. position: relative;
  417. margin-right: 16rpx;
  418. &:nth-child(3n) {
  419. margin-right: 0;
  420. }
  421. &:nth-child(n + 4) {
  422. margin-top: 16rpx;
  423. }
  424. .thumbnail {
  425. width: 100%;
  426. height: 100%;
  427. }
  428. .defaultFileItem {
  429. position: relative;
  430. width: 100%;
  431. height: 100%;
  432. .playBtn {
  433. position: absolute;
  434. top: 50%;
  435. left: 50%;
  436. transform: translate(-50%, -50%);
  437. width: 48rpx;
  438. height: 48rpx;
  439. }
  440. }
  441. .deleteFile {
  442. width: 20px;
  443. height: 20px;
  444. position: absolute;
  445. top: 0;
  446. right: 0;
  447. z-index: 10;
  448. }
  449. }
  450. .addFileBtn {
  451. background-image: url(../../static/image/addFile.png);
  452. background-position: center center;
  453. background-repeat: no-repeat;
  454. background-color: #ffffff;
  455. background-size: 100%;
  456. }
  457. }
  458. .formBox {
  459. margin-top: 56rpx;
  460. .phoneBox{
  461. display: flex;
  462. padding-bottom: 22px;
  463. justify-content: space-between;
  464. .label{
  465. font-size: 13px;
  466. color: #666666;
  467. padding-left: 5px;
  468. position: relative;
  469. &::before{
  470. content: '*';
  471. position: absolute;
  472. left: -1px;
  473. top: 0;
  474. color: red;
  475. }
  476. }
  477. }
  478. .textRight {
  479. /deep/.is-input-border {
  480. border: none;
  481. text-align: right;
  482. }
  483. /deep/.is-disabled {
  484. background-color: #ffffff !important;
  485. }
  486. }
  487. .selectInputBox {
  488. height: 100%;
  489. display: flex;
  490. justify-content: flex-end;
  491. align-items: center;
  492. .rightIcon {
  493. margin-left: 16rpx;
  494. }
  495. .addressNoText {
  496. color: #d5d5d5;
  497. }
  498. }
  499. }
  500. }
  501. .popup-content {
  502. height: 100vh;
  503. }
  504. .publishBtn {
  505. width: 686rpx;
  506. height: 44px;
  507. line-height: 44px;
  508. text-align: center;
  509. margin: 15px auto 0 auto;
  510. background-color: #007aff;
  511. border-radius: 8px;
  512. color: #ffffff;
  513. }
  514. .popupBottom {
  515. position: fixed;
  516. bottom: 0;
  517. left: 0;
  518. width: 100%;
  519. height: 100%;
  520. z-index: 100;
  521. display: flex;
  522. flex-direction: column;
  523. .popupBg {
  524. position: fixed;
  525. left: 0;
  526. top: 0;
  527. width: 100%;
  528. height: 100%;
  529. background: rgba(0, 0, 0, 0.5);
  530. }
  531. .popupContent {
  532. height: 40%;
  533. margin-top: auto;
  534. background-color: #fff;
  535. position: relative;
  536. z-index: 11;
  537. }
  538. .popupSwiperBg {
  539. position: fixed;
  540. left: 0;
  541. top: 0;
  542. width: 100%;
  543. height: 100%;
  544. background: rgba(0, 0, 0, 0.5);
  545. display: flex;
  546. align-items: center;
  547. }
  548. .swiperDotBox {
  549. display: flex;
  550. justify-content: center;
  551. align-items: center;
  552. height: 100%;
  553. width: 100%;
  554. flex-direction: column;
  555. .swiperBox {
  556. height: 85vh;
  557. width: 100%;
  558. }
  559. .swiperFileItem {
  560. width: 100%;
  561. height: 100%;
  562. }
  563. .swiperAudioBox {
  564. width: 100%;
  565. height: 100%;
  566. position: relative;
  567. }
  568. .swiperAudioItem {
  569. position: absolute;
  570. top: 50%;
  571. left: 50%;
  572. transform: translate(-50%, -50%);
  573. z-index: 111;
  574. }
  575. .swiperPage {
  576. position: absolute;
  577. bottom: 20rpx;
  578. width: 100%;
  579. height: 30px;
  580. text-align: center;
  581. line-height: 30px;
  582. color: #ffffff;
  583. }
  584. }
  585. }
  586. }
  587. </style>