goods.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. <template>
  2. <view class="goods-page">
  3. <view class="goods-cont">
  4. <scroll-view scroll-y class="menus">
  5. <view class="item" v-for="item in goodsList" :key="item.goodsTypeId" @click="handMenuChange(item)"
  6. :class="{ select: item.goodsTypeId == active }">
  7. <view class="new" v-if="item.children != null && item.children.filter(i => i.isNew).length > 0">上新</view>
  8. <view class="name">{{ item.goodsTypeName }}</view>
  9. </view>
  10. </scroll-view>
  11. <scroll-view scroll-with-animation scroll-y class="list-wrap" @scroll="asideScroll" :scroll-top="tabScrollTop">
  12. <view class="class" v-for="item in goodsList" :key="item.goodsTypeId" :id="'goods' + item.goodsTypeId">
  13. <view class="title">{{ item.goodsTypeName }}</view>
  14. <view class="list">
  15. <view class="goods" v-for="(goods,index) in item.children" :key="goods.goodsId">
  16. <view class="cover">
  17. <image class="img" :src="goods.pictureUrl"></image>
  18. </view>
  19. <view class="cont">
  20. <view class="name">{{ goods.goodsName }}</view>
  21. <view class="desc">
  22. 月销 {{ goods.salesCount }} 库存 {{ goods.inventory }}
  23. </view>
  24. <view class="price-wrap">
  25. <view class="price" v-if="goods.salePrice < goods.costPrice">¥{{ moneyConverter(goods.salePrice) }}</view>
  26. <view class="costprice" v-if="goods.salePrice < goods.costPrice">¥{{ moneyConverter(goods.costPrice) }}</view>
  27. <view class="price" v-if="goods.salePrice >= goods.costPrice">¥{{ moneyConverter(goods.costPrice) }}</view>
  28. <view class="old" v-if="goods.newGoodsFlg" @click="setNewGoods(goods)">取消新品</view>
  29. <view class="new" v-if="!goods.newGoodsFlg" @click="setNewGoods(goods)">设置新品</view>
  30. </view>
  31. <view class="tools">
  32. <view class="btn" @click="updateGoods(goods.goodsId)">编辑</view>
  33. <view class="btn bgbtn" v-if="goods.status == 0" @click="updateStatus(goods.status,goods.goodsId)">上架</view>
  34. <view class="btn" v-if="goods.status == 1" @click="updateStatus(goods.status,goods.goodsId)">下架</view>
  35. <view class="btn" v-if="index!=0" @click="moveUp(goods.goodsId)">前移</view>
  36. <view class="btn-red bgbtn-red" v-if="goods.status == 0" @click="deleteGoods(goods.goodsId)">删除</view>
  37. </view>
  38. </view>
  39. </view>
  40. </view>
  41. </view>
  42. <!-- <view class="empty-block" :style="{ height: emptyBlockHeight + 'px' }"></view> -->
  43. </scroll-view>
  44. </view>
  45. <view class="footer-btns">
  46. <view class="btn" @click="handChangeClass">分类管理</view>
  47. <view class="btn" @click="updateGoods()">添加商品</view>
  48. </view>
  49. <!-- <u-picker :show="show" :columns="columns" keyName="label" @cancel="cancelNew" @confirm="confirmNewGoods"></u-picker> -->
  50. <popupPickerView :list="columns" itemKey="id" itemName="label" ref="popupPickerView" @callback="confirmNewGoods" />
  51. </view>
  52. </template>
  53. <script>
  54. import {mapState} from 'vuex';
  55. import popupPickerView from '../components/popup-picker-view.vue';
  56. export default {
  57. components: {
  58. popupPickerView
  59. },
  60. data () {
  61. return {
  62. getGoodsListUrl: '/storeGoodsManageApi/getStoreGoodsList',
  63. moveUpUrl: '/storeGoodsManageApi/goodsMoveUp',
  64. changeStatusUrl: '/storeGoodsManageApi/changeStatus',
  65. setNewFlagUrl: '/storeGoodsManageApi/setNewFlag',
  66. deleteUrl: '/storeGoodsManageApi/deleteGoods',
  67. active: '0',
  68. goodsList: [],
  69. columns: [
  70. [{
  71. label: '7天',
  72. id: 7
  73. }, {
  74. label: '15天',
  75. id: 15
  76. }, {
  77. label: '30天',
  78. id: 30
  79. }]
  80. ],
  81. show: false,
  82. goodsId: '',
  83. emptyBlockHeight: 0,
  84. tabScrollTop: 0,
  85. sizeCalcState: false,
  86. changeState: true,
  87. windowHeight: '',
  88. tabbar: true,
  89. }
  90. },
  91. computed: {
  92. ...mapState(['storeInfo'])
  93. },
  94. methods: {
  95. handCheckGoodsType () {
  96. this.$refs.popupPickerView.show()
  97. },
  98. loadData() {
  99. uni.showLoading({
  100. title: '加载中'
  101. });
  102. var dateStr = (new Date()).getTime();
  103. var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
  104. this.requst({
  105. url: this.getGoodsListUrl,
  106. data: {
  107. timestamp: dateStr,
  108. sign: sign,
  109. storeId: this.storeInfo.storeId,
  110. storeUserId: this.storeInfo.storeUserId
  111. },
  112. success: res => {
  113. if (res.data.rtnBean.rtnCode == 0) {
  114. let obj = res.data.obj;
  115. this.goodsList = res.data.list;
  116. // uni.setNavigationBarTitle({
  117. // title: this.info.storeName
  118. // });
  119. setTimeout(()=>{
  120. this.calcSize();
  121. }, 300)
  122. }
  123. uni.hideLoading();
  124. },
  125. fail: (e) => {
  126. console.log("接口调用失败:" + JSON.stringify(e));
  127. uni.stopPullDownRefresh()
  128. uni.hideLoading();
  129. }
  130. })
  131. },
  132. cancelNew() {
  133. // 取消操作
  134. this.goodsId = '';
  135. this.show = false
  136. },
  137. updateStatus (status,goodsId) {
  138. if (status == 0){
  139. status = 1;
  140. } else {
  141. status = 0
  142. }
  143. uni.showLoading({
  144. title: '加载中'
  145. });
  146. var dateStr = (new Date()).getTime();
  147. var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
  148. this.requst({
  149. url: this.changeStatusUrl,
  150. data: {
  151. timestamp: dateStr,
  152. sign: sign,
  153. storeId: this.storeInfo.storeId,
  154. storeUserId: this.storeInfo.storeUserId,
  155. status: status,
  156. goodsId: goodsId
  157. },
  158. success: res => {
  159. if (res.data.rtnBean.rtnCode == 0) {
  160. this.loadData();
  161. // uni.setNavigationBarTitle({
  162. // title: this.info.storeName
  163. // });
  164. }
  165. uni.hideLoading();
  166. },
  167. fail: (e) => {
  168. console.log("接口调用失败:" + JSON.stringify(e));
  169. uni.hideLoading();
  170. }
  171. })
  172. },
  173. moveUp (goodsId) {
  174. uni.showLoading({
  175. title: '加载中'
  176. });
  177. var dateStr = (new Date()).getTime();
  178. var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
  179. this.requst({
  180. url: this.moveUpUrl,
  181. data: {
  182. timestamp: dateStr,
  183. sign: sign,
  184. storeId: this.storeInfo.storeId,
  185. storeUserId: this.storeInfo.storeUserId,
  186. goodsId: goodsId
  187. },
  188. success: res => {
  189. if (res.data.rtnBean.rtnCode == 0) {
  190. this.loadData();
  191. }
  192. uni.hideLoading();
  193. },
  194. fail: (e) => {
  195. console.log("接口调用失败:" + JSON.stringify(e));
  196. uni.hideLoading();
  197. }
  198. })
  199. },
  200. deleteGoods(goodsId) {
  201. uni.showLoading({
  202. title: '加载中'
  203. });
  204. var dateStr = (new Date()).getTime();
  205. var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
  206. this.requst({
  207. url: this.deleteUrl,
  208. data: {
  209. timestamp: dateStr,
  210. sign: sign,
  211. goodsId: goodsId,
  212. storeUserId: this.storeInfo.storeUserId,
  213. },
  214. success: res => {
  215. if (res.data.rtnBean.rtnCode == 0) {
  216. this.loadData();
  217. }
  218. uni.hideLoading();
  219. },
  220. fail: (e) => {
  221. console.log("接口调用失败:" + JSON.stringify(e));
  222. uni.hideLoading();
  223. }
  224. })
  225. },
  226. confirmNewGoods(e) {
  227. let dayCount = e.id;
  228. this.handDelete(dayCount);
  229. },
  230. handDelete(dayCount) {
  231. //设置新品
  232. uni.showLoading({
  233. title: '加载中'
  234. });
  235. var dateStr = (new Date()).getTime();
  236. var sign = this.getSign('' + this.storeInfo.storeUserId + dateStr);
  237. this.requst({
  238. url: this.setNewFlagUrl,
  239. data: {
  240. timestamp: dateStr,
  241. sign: sign,
  242. storeId: this.storeInfo.storeId,
  243. storeUserId: this.storeInfo.storeUserId,
  244. goodsId: this.goodsId,
  245. dayCount: dayCount || ''
  246. },
  247. success: res => {
  248. if (res.data.rtnBean.rtnCode == 0) {
  249. this.loadData();
  250. }
  251. this.show = false
  252. uni.hideLoading();
  253. },
  254. fail: (e) => {
  255. console.log("接口调用失败:" + JSON.stringify(e));
  256. uni.hideLoading();
  257. }
  258. })
  259. },
  260. setNewGoods (goods) {
  261. this.goodsId = goods.goodsId;
  262. if (goods.newGoodsFlg != undefined && goods.newGoodsFlg == 1) {
  263. this.handDelete();
  264. } else {
  265. this.$refs.popupPickerView.show()
  266. }
  267. },
  268. handMenuChange (item) {
  269. if (!this.sizeCalcState) {
  270. this.calcSize();
  271. }
  272. this.active = item.goodsTypeId;
  273. let index = this.goodsList.findIndex(sitem => sitem.goodsTypeId === item.goodsTypeId);
  274. this.tabScrollTop = this.goodsList[index].top;
  275. this.changeState = false;
  276. },
  277. handChangeClass () {
  278. uni.navigateTo({
  279. url: '/pages/goods/goods-type'
  280. });
  281. },
  282. updateGoods (id) {
  283. if (id != null && id != undefined) {
  284. uni.navigateTo({
  285. url: '/pages/goods/goods-edit?goodsId=' + id
  286. });
  287. } else {
  288. uni.navigateTo({
  289. url: '/pages/goods/goods-edit'
  290. });
  291. }
  292. },
  293. //右侧栏滚动
  294. asideScroll(e) {
  295. if (!this.sizeCalcState) {
  296. this.calcSize();
  297. }
  298. let scrollTop = e.detail.scrollTop;
  299. let tabs = this.goodsList.filter(item => item.top <= scrollTop).reverse();
  300. if (this.changeState && tabs.length > 0) {
  301. this.active = tabs[0].goodsTypeId;
  302. }
  303. this.changeState = true;
  304. },
  305. //计算右侧栏每个tab的高度等信息
  306. calcSize() {
  307. // 获取视口高度
  308. const wrap = uni.createSelectorQuery().in(this);
  309. let wH = 0
  310. wrap.select('.list-wrap').boundingClientRect(data => {
  311. wH = data.height
  312. }).exec();
  313. let h = 0;
  314. if(this.goodsList != '' || this.goodsList != null){
  315. this.goodsList.forEach((item, idx) => {
  316. let view = uni.createSelectorQuery().select("#goods" + item.goodsTypeId);
  317. view.fields({
  318. size: true
  319. }, data => {
  320. item.top = h;
  321. h += data.height;
  322. item.bottom = h;
  323. // 当遍历到最后一个分类商品时获取对应商品元素的top值 减去 视口高度 赋值给 商品列表底部高度补位元素
  324. // if (idx == this.goodsList.length - 1) {
  325. // this.emptyBlockHeight = item.top - wH - 2
  326. // }
  327. }).exec();
  328. })
  329. this.sizeCalcState = true;
  330. } else {
  331. return
  332. }
  333. },
  334. },
  335. onReady(){
  336. setTimeout(()=>{
  337. this.calcSize();
  338. }, 300)
  339. },
  340. onShow () {
  341. this.loadData()
  342. // uni.$once('goods', goods => {
  343. // console.log('goods', goods);
  344. // })
  345. uni.getSystemInfo({
  346. success: (res) => {
  347. this.windowHeight = res.windowHeight;
  348. }
  349. });
  350. uni.onWindowResize((res) => {
  351. if (res.size.windowHeight < this.windowHeight) {
  352. this.tabbar = false
  353. } else {
  354. this.tabbar = true
  355. }
  356. });
  357. },
  358. onLoad () {
  359. }
  360. }
  361. </script>
  362. <style lang="scss" scoped>
  363. .goods-page {
  364. background-color: #f5f5f5;
  365. width: 100vw;
  366. height: 100vh;
  367. }
  368. .footer-btns {
  369. width: 100vw;
  370. background-color: #fff;
  371. @include between;
  372. margin-bottom: env(safe-area-inset-bottom);
  373. margin-bottom: constant(safe-area-inset-bottom);
  374. .btn {
  375. width: 49.5vw;
  376. height: 80rpx;
  377. background-color: $theme-color;
  378. color: #fff;
  379. @include flexCenter;
  380. }
  381. }
  382. .goods-cont, .menus, .list-wrap {
  383. height: calc(100vh - 80rpx - env(safe-area-inset-bottom));
  384. height: calc(100vh - 80rpx - constant(safe-area-inset-bottom));
  385. }
  386. .goods-cont {
  387. width: 100vw;
  388. @include flexStart;
  389. border-top: 2rpx solid #f5f5f5;
  390. font-size: $font-base;
  391. }
  392. .menus {
  393. width: 190rpx;
  394. .item {
  395. height: 140rpx;
  396. position: relative;
  397. padding: 0 20rpx;
  398. box-sizing: border-box;
  399. @include flexStart;
  400. .new {
  401. position: absolute;
  402. right: 0;
  403. top: 0;
  404. width: 80rpx;
  405. font-size: $font-sm;
  406. color: #fff;
  407. background-color: #ff5000;
  408. @include flexCenter;
  409. border-radius: 20rpx 0 20rpx 0;
  410. }
  411. }
  412. .item.select {
  413. background-color: #fff;
  414. }
  415. }
  416. .list-wrap {
  417. background-color: #fff;
  418. width: calc(100vw - 190rpx);
  419. padding: 20rpx;
  420. box-sizing: border-box;
  421. .title {
  422. text-align: center;
  423. font-weight: bold;
  424. color: $title-color;
  425. margin-bottom: 20rpx;
  426. }
  427. .list {
  428. border-bottom: 2rpx solid #ececec;
  429. margin-bottom: 20rpx;
  430. padding-bottom: 20rpx;
  431. }
  432. .cover, .img {
  433. width: 160rpx;
  434. height: 160rpx;
  435. border-radius: 10rpx;
  436. }
  437. .cover {
  438. margin-right: 20rpx;
  439. }
  440. .goods {
  441. @include flexStart(flex-start);
  442. margin-bottom: 20rpx;
  443. }
  444. .cont {
  445. width: calc(100% - 180rpx);
  446. }
  447. .name {
  448. width: 100%;
  449. height: 80rpx;
  450. text-overflow: -o-ellipsis-lastline;
  451. overflow: hidden;
  452. text-overflow: ellipsis;
  453. display: -webkit-box;
  454. -webkit-line-clamp: 2;
  455. -webkit-box-orient: vertical;
  456. color: $text-color;
  457. margin-bottom: 6rpx;
  458. }
  459. .desc {
  460. color: $desc-color;
  461. font-size: $font-sm;
  462. margin-bottom: 6rpx;
  463. }
  464. .price-wrap {
  465. @include flexStart;
  466. }
  467. .price {
  468. margin-right: 20rpx;
  469. color: #ff5000;
  470. }
  471. .costprice {
  472. text-decoration: line-through;
  473. margin-right: 20rpx;
  474. color: $desc-color;
  475. }
  476. .new {
  477. padding: 0 10rpx;
  478. background-color: $desc-color;
  479. color: #fff;
  480. @include flexCenter;
  481. border-radius: 10rpx;
  482. font-size: $font-sm;
  483. }
  484. .old {
  485. padding: 0 10rpx;
  486. background-color: #ff5000;
  487. color: #fff;
  488. @include flexCenter;
  489. border-radius: 10rpx;
  490. font-size: $font-sm;
  491. }
  492. .tools {
  493. @include flexStart;
  494. margin-top: 16rpx;
  495. .btn {
  496. margin-right: 16rpx;
  497. padding: 2rpx 10rpx;
  498. border: 2rpx solid $theme-color;
  499. color: $theme-color;
  500. font-size: $font-sm;
  501. }
  502. .btn:last-child {
  503. margin-right: 0;
  504. }
  505. .bgbtn {
  506. background-color: $theme-color;
  507. color: #fff;
  508. }
  509. .btn-red {
  510. margin-right: 16rpx;
  511. padding: 2rpx 10rpx;
  512. border: 2rpx solid red;
  513. color: $theme-color;
  514. font-size: $font-sm;
  515. margin-right: 0;
  516. }
  517. .bgbtn-red {
  518. background-color: red;
  519. color: #fff;
  520. }
  521. }
  522. }
  523. </style>