menu.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. <template>
  2. <view class="u-wrap ">
  3. <u-navbar :is-back="false" title="菜单管理"></u-navbar>
  4. <view class="flex bg-white" style="padding: 30rpx 10rpx;">
  5. <view class="" style="width: 20%;">
  6. <u-button @click="operateCategory(null)" size="mini" type="warning" plain
  7. class=" text-sm text-red flex text-bold">
  8. <text>添加分类</text>
  9. <text class="cuIcon-add u-m-l-4 center"></text>
  10. </u-button>
  11. </view>
  12. <view v-if="$isNotEmpty(goodsList[current])" @click="operateGoods(null)" class="center bg-white flex"
  13. style="width: 80%;color: #007AFF;">
  14. <text class="cuIcon-roundadd margin-right-10"></text>
  15. <view class="">
  16. <text>添加</text>
  17. <text
  18. v-if="goodsList[current].goodsCategory.name">【{{goodsList[current].goodsCategory.name}}】</text>
  19. <text>分类下商品</text>
  20. </view>
  21. </view>
  22. </view>
  23. <view class="u-menu-wrap">
  24. <scroll-view scroll-y scroll-with-animation class="u-tab-view menu-scroll-view" :scroll-top="scrollTop">
  25. <view v-for="(item,index) in goodsList" :key="index" class="u-tab-item"
  26. :class="[current==index ? 'u-tab-item-active' : '']" :data-current="index"
  27. @tap.stop="swichMenu(index)">
  28. <view class="">
  29. <text class="u-line-1 u-m-r-4"
  30. :style="!item.goodsCategory.showStatus?'text-decoration: line-through;color: #bababa':''">{{item.goodsCategory.name}}</text>
  31. <u-icon color="#EF9944" @click="operateCategory(item.goodsCategory)" name="edit-pen-fill">
  32. </u-icon>
  33. </view>
  34. </view>
  35. </scroll-view>
  36. <block v-for="(item,index) in goodsList" :key="index">
  37. <scroll-view scroll-y class="right-box" v-if="current==index">
  38. <view class="page-view">
  39. <view class="class-item">
  40. <view class="item-title">
  41. <text>{{item.goodsCategory.name}}</text>
  42. </view>
  43. <view class="item-container">
  44. <view class="thumb-box" v-for="(goods, index1) in item.goods" :key="index1">
  45. <image @click="$util.preview(goods.image)" class="item-menu-image"
  46. :src="goods.image" mode=""></image>
  47. <view class="item-content">
  48. <view class="name flex justify-between ">
  49. <view class="text-cut-1 " style="width: 75%;">
  50. <text>{{goods.name}}</text>
  51. </view>
  52. <view class="text-red text-sm" @click="operateGoods(goods)">
  53. <text>编辑</text>
  54. <text class="cuIcon-edit u-m-l-2"></text>
  55. </view>
  56. </view>
  57. <view class="tips">
  58. <view class="text-cut-1 " style="width: 75%;">
  59. {{ goods.description}}
  60. </view>
  61. <view class="text-blue text-sm" @click="operateSku(goods)">
  62. <text>规格</text>
  63. <text class="cuIcon-goods u-m-l-2"></text>
  64. </view>
  65. </view>
  66. <view class="price_and_action">
  67. <text class="price">¥{{ goods.defaultPrice / 100 }}</text>
  68. <text v-if="!goods.isSell"
  69. class="margin-left-20 text-sm text-red">已下架</text>
  70. </view>
  71. </view>
  72. </view>
  73. </view>
  74. </view>
  75. </view>
  76. </scroll-view>
  77. </block>
  78. </view>
  79. <!-- 分类 -->
  80. <u-popup borderRadius="12" v-model="categoryShow" mode="center" width="80%" :closeable="true">
  81. <view class="center padding-30 flex-direction">
  82. <text class="text-center text-lg" v-text="categoryEdit?'修改分类':'添加分类'"></text>
  83. <view class="margin-top-30">
  84. <u-form :model="categoryForm" ref="uForm" label-width="140">
  85. <u-form-item label="分类名称">
  86. <u-input :clearable="false" v-model="categoryForm.name" />
  87. </u-form-item>
  88. <u-form-item label="显示顺序">
  89. <u-number-box v-model="categoryForm.displayOrder" :min="1" :max="100"></u-number-box>
  90. </u-form-item>
  91. <u-form-item label="是否显示">
  92. <view class="center" style="justify-content: flex-start;">
  93. <u-switch active-color="#FF9447" v-model="categoryForm.showStatus" active-value="1"
  94. inactive-value="0"></u-switch>
  95. </view>
  96. </u-form-item>
  97. </u-form>
  98. </view>
  99. <view class="center " style="margin-top: 60rpx;width: 100%;justify-content: space-around;">
  100. <block v-if="categoryEdit">
  101. <view @click="deleteCategory" class="cu-btn flex text-lg line-red radius"
  102. style="padding: 40rpx 0;width: 46%;">
  103. 删除
  104. </view>
  105. <view @click="submitCategory" class="cu-btn flex text-lg bg-base radius"
  106. style="padding: 40rpx 0;width: 46%;">
  107. 确认
  108. </view>
  109. </block>
  110. <view v-else @click="submitCategory" class="cu-btn flex text-lg bg-base radius"
  111. style="padding: 40rpx 0;width: 90%;">
  112. 确认
  113. </view>
  114. </view>
  115. </view>
  116. </u-popup>
  117. <!-- 商品 -->
  118. <u-popup borderRadius="12" v-model="goodsShow" mode="center" width="90%" :closeable="true" close-icon-pos="top-left">
  119. <view class="bg-white center flex-direction" style="padding: 30rpx 20rpx;">
  120. <text class="text-center text-bold text-lg" v-text="goodsEdit?'修改商品':'添加商品'"></text>
  121. <view class="margin-top-10">
  122. <u-form :error-type="['toast']" :model="goodsForm" ref="uGoodsForm" label-width="160">
  123. <u-form-item prop="image" :required="true" label="商品图片" label-position="top">
  124. <view class="" style="margin-top: -40rpx;margin-bottom: -20rpx;">
  125. <mp-upload-img ref="mpUploadImg" @delImg="delGoodsImg" @click='uploadGoodsImg'
  126. :count="1" col="3"></mp-upload-img>
  127. </view>
  128. </u-form-item>
  129. <u-form-item :required="true" label="所属分类">
  130. <u-input :clearable="false" disabled placeholder-style="color:#d8d8d8"
  131. v-model="goodsForm.goodsCategoryName" />
  132. </u-form-item>
  133. <u-form-item :required="true" label="商品名称" prop="name">
  134. <u-input :clearable="false" placeholder-style="color:#d8d8d8" v-model="goodsForm.name" />
  135. </u-form-item>
  136. <u-form-item :required="true" label="商品单价">
  137. <u-input :clearable="false" placeholder-style="color:#d8d8d8" v-model="price" />
  138. <text slot="right">元</text>
  139. </u-form-item>
  140. <u-form-item label="显示顺序">
  141. <u-number-box v-model="goodsForm.displayOrder" ::min="1" :max="100"></u-number-box>
  142. </u-form-item>
  143. <u-form-item label="是否在卖">
  144. <view class="center" style="justify-content: flex-start;">
  145. <u-switch active-color="#FF9447" v-model="goodsForm.isSell" active-value="1"
  146. inactive-value="0"></u-switch>
  147. </view>
  148. </u-form-item>
  149. <u-form-item label="商品描述">
  150. <u-input :clearable="false" placeholder-style="color:#d8d8d8"
  151. v-model="goodsForm.description" placeholder="请输入商品描述" />
  152. </u-form-item>
  153. </u-form>
  154. </view>
  155. <view class="center " style="margin-top: 60rpx;width: 100%;justify-content: space-around;">
  156. <block v-if="goodsEdit">
  157. <view @click="deleteGoods" class="cu-btn flex text-lg line-red radius"
  158. style="padding: 40rpx 0;width: 46%;">
  159. 删除
  160. </view>
  161. <view @click="submitGoods" class="cu-btn flex text-lg bg-base radius"
  162. style="padding: 40rpx 0;width: 46%;">
  163. 确认
  164. </view>
  165. </block>
  166. <view v-else @click="submitGoods" class="cu-btn flex text-lg bg-base radius"
  167. style="padding: 40rpx 0;width: 90%;">
  168. 确认
  169. </view>
  170. </view>
  171. </view>
  172. </u-popup>
  173. <toast ref="toast"></toast>
  174. <u-tabbar :height="tabbar.height" @change="tabChange" v-model="tabbarCurr" :icon-size="tabbar.iconSize"
  175. :active-color="tabbar.activeColor" :mid-button-size="tabbar.MinButtonSize" :list="tabbar.list">
  176. </u-tabbar>
  177. </view>
  178. </template>
  179. <script>
  180. import {
  181. tabbar
  182. } from "../../assert/js/tarbar.js"
  183. import mpUploadImg from "@/components/mp-uploadImg/mp-uploadImg.vue"
  184. export default {
  185. components: {
  186. mpUploadImg
  187. },
  188. data() {
  189. return {
  190. //tabbar
  191. tabbarCurr: 0,
  192. tabbar: tabbar,
  193. goodsList: [],
  194. //分类
  195. operateCategoryItem: {},
  196. categoryEdit: false,
  197. categoryShow: false,
  198. categoryForm: {
  199. id: '',
  200. shopId: '',
  201. name: '',
  202. displayOrder: 10,
  203. //是否显示
  204. showStatus: 1,
  205. },
  206. //商品 begin
  207. imgList: [],
  208. price: 0,
  209. operateGoodsItem: {},
  210. goodsEdit: false,
  211. goodsShow: false,
  212. goodsForm: {
  213. id: '',
  214. shopId: '',
  215. goodsCategoryName: '',
  216. name: '',
  217. displayOrder: 10,
  218. defaultPrice: 0,
  219. isSell: 1,
  220. image: '',
  221. description: null
  222. },
  223. goodsRules: {
  224. image: [{
  225. required: true,
  226. message: '请上传商品图片',
  227. trigger: ['blur', 'change']
  228. }],
  229. name: [{
  230. required: true,
  231. message: '请输入商品名称',
  232. trigger: ['blur', 'change']
  233. }],
  234. },
  235. //商品 end
  236. // 菜单
  237. scrollTop: 0, //tab标题的滚动条位置
  238. current: 0, // 预设当前项的值
  239. menuHeight: 0, // 左边菜单的高度
  240. menuItemHeight: 0, // 左边菜单item的高度
  241. }
  242. },
  243. onLoad() {
  244. this.getGoodsList()
  245. },
  246. onReady() {
  247. this.$refs.uGoodsForm.setRules(this.goodsRules);
  248. },
  249. methods: {
  250. tabChange(index){
  251. if (index==1) {
  252. uni.redirectTo({
  253. url:"/pagesGoods/pages/order/order"
  254. })
  255. }else if (index==2) {
  256. uni.redirectTo({
  257. url:"/pagesGoods/pages/my-shop/my-shop"
  258. })
  259. }
  260. },
  261. getGoodsList() {
  262. let params = {
  263. shopId: this.vuex_shopId
  264. }
  265. this.$api.goods.list(params).then(res => {
  266. this.goodsList = res.data
  267. })
  268. },
  269. //分类 begin
  270. operateCategory(item) {
  271. this.operateCategoryItem = item
  272. this.categoryShow = true
  273. if (this.$isEmpty(item)) {
  274. //添加分类
  275. this.reSetCategoryForm()
  276. this.categoryEdit = false
  277. return
  278. }
  279. //修改分类
  280. this.categoryEdit = true
  281. this.$util.objectCopy(this.categoryForm, item)
  282. },
  283. async submitCategory() {
  284. if (this.$isEmpty(this.categoryForm.name)) {
  285. this.$u.toast('请输入类目名称!')
  286. return
  287. }
  288. if (!this.categoryEdit) {
  289. let flag = this.goodsList.some(item => item.goodsCategory.name == this.categoryForm.name)
  290. if (flag) {
  291. this.$u.toast('已有该名称的类目!')
  292. return
  293. }
  294. }
  295. this.categoryForm.shopId = this.vuex_shopId
  296. let res = await this.$api.goodsCategory.submit(this.categoryForm)
  297. if (res.success) {
  298. this.$u.toast('操作成功')
  299. this.getGoodsList()
  300. this.categoryShow = false
  301. }
  302. },
  303. deleteCategory() {
  304. this.$dialog.showModal('确定删除该分类?').then(() => {
  305. this.$api.goodsCategory.remove(this.operateCategoryItem.id).then(res => {
  306. if (res.success) {
  307. this.$u.toast('删除成功')
  308. this.reloadGoods()
  309. this.current = 0
  310. this.categoryShow = false
  311. this.getGoodsList()
  312. }
  313. })
  314. })
  315. },
  316. reSetCategoryForm() {
  317. this.categoryForm = {
  318. id: '',
  319. shopId: '',
  320. name: '',
  321. displayOrder: 1,
  322. //是否显示
  323. showStatus: 1,
  324. }
  325. },
  326. //分类 end
  327. //商品begin
  328. reloadGoods(){
  329. let params={
  330. shopId:this.vuex_shopId
  331. }
  332. this.$api.goods.reload(params)
  333. },
  334. uploadGoodsImg(e) {
  335. this.goodsForm.image = e[0]
  336. },
  337. operateGoods(item) {
  338. this.goodsForm.goodsCategoryName = this.goodsList[this.current].goodsCategory.name
  339. this.operateGoodsItem = item
  340. this.goodsShow = true
  341. if (this.$isEmpty(item)) {
  342. //添加商品
  343. this.reSetGoodsData()
  344. this.goodsEdit = false
  345. return
  346. }
  347. //修改商品
  348. this.price = item.defaultPrice / 100
  349. this.imgList = [item.image]
  350. this.$refs.mpUploadImg.changeImgList(this.imgList)
  351. this.goodsEdit = true
  352. this.$util.objectCopy(this.goodsForm, item)
  353. },
  354. delGoodsImg(item) {
  355. this.imgList = item[0]
  356. this.goodsForm.image = this.imgList
  357. },
  358. reSetGoodsData() {
  359. this.price = 0
  360. this.imgList = []
  361. this.$refs.mpUploadImg.changeImgList(this.imgList)
  362. let goodsCategoryName = this.goodsForm.goodsCategoryName
  363. this.goodsForm = {
  364. id: '',
  365. shopId: '',
  366. goodsCategoryName,
  367. name: '',
  368. displayOrder: 1,
  369. defaultPrice: 0,
  370. isSell: 1,
  371. image: '',
  372. description: null
  373. }
  374. },
  375. submitGoods() {
  376. this.$refs.uGoodsForm.validate(valid => {
  377. if (valid) {
  378. this.goodsForm.defaultPrice = this.price * 100
  379. if (this.$isEmpty(this.goodsForm.defaultPrice)) {
  380. this.$u.toast('请输入商品单价')
  381. return
  382. }
  383. this.doSubmitGoods()
  384. } else {
  385. console.log('验证失败');
  386. }
  387. });
  388. },
  389. async doSubmitGoods() {
  390. this.goodsForm.shopId = this.vuex_shopId
  391. let res = await this.$api.goods.submit(this.goodsForm)
  392. if (res.success) {
  393. this.$u.toast('操作成功')
  394. this.getGoodsList()
  395. this.goodsShow = false
  396. }
  397. },
  398. deleteGoods() {
  399. this.$dialog.showModal('确定删除该商品?').then(() => {
  400. this.$api.goods.remove(this.operateGoodsItem.id).then(res => {
  401. if (res.success) {
  402. this.$u.toast('删除成功')
  403. this.reloadGoods()
  404. this.goodsShow = false
  405. this.getGoodsList()
  406. }
  407. })
  408. })
  409. },
  410. //商品end
  411. //商品属性begin
  412. operateSku(goods) {
  413. let params = {
  414. price:goods.defaultPrice,
  415. goodsName: goods.name,
  416. goodsId: goods.id
  417. }
  418. uni.navigateTo({
  419. url: "/pagesGoods/pages/menu/sku" + this.$u.queryParams(params),
  420. })
  421. },
  422. //商品属性end
  423. //菜单 begin
  424. // 点击左边的栏目切换
  425. async swichMenu(index) {
  426. if (index == this.current) return;
  427. this.current = index;
  428. // 如果为0,意味着尚未初始化
  429. if (this.menuHeight == 0 || this.menuItemHeight == 0) {
  430. await this.getElRect('menu-scroll-view', 'menuHeight');
  431. await this.getElRect('u-tab-item', 'menuItemHeight');
  432. }
  433. // 将菜单菜单活动item垂直居中
  434. this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
  435. },
  436. // 获取一个目标元素的高度
  437. getElRect(elClass, dataVal) {
  438. new Promise((resolve, reject) => {
  439. const query = uni.createSelectorQuery().in(this);
  440. query.select('.' + elClass).fields({
  441. size: true
  442. }, res => {
  443. // 如果节点尚未生成,res值为null,循环调用执行
  444. if (!res) {
  445. setTimeout(() => {
  446. this.getElRect(elClass);
  447. }, 10);
  448. return;
  449. }
  450. this[dataVal] = res.height;
  451. }).exec();
  452. })
  453. }
  454. // 菜单end
  455. }
  456. }
  457. </script>
  458. <style lang="scss" scoped>
  459. .u-wrap {
  460. height: calc(100vh);
  461. /* #ifdef H5 */
  462. height: calc(100vh - var(--window-top));
  463. /* #endif */
  464. display: flex;
  465. flex-direction: column;
  466. }
  467. .u-menu-wrap {
  468. flex: 1;
  469. display: flex;
  470. overflow: hidden;
  471. }
  472. .u-tab-view {
  473. width: 200rpx;
  474. height: 100%;
  475. }
  476. .u-tab-item {
  477. height: 110rpx;
  478. background: #f6f6f6;
  479. box-sizing: border-box;
  480. display: flex;
  481. align-items: center;
  482. justify-content: center;
  483. font-size: 26rpx;
  484. color: #444;
  485. font-weight: 400;
  486. line-height: 1;
  487. }
  488. .u-tab-item-active {
  489. position: relative;
  490. color: #000;
  491. font-size: 30rpx;
  492. font-weight: 600;
  493. background: #fff;
  494. }
  495. .u-tab-item-active::before {
  496. content: "";
  497. position: absolute;
  498. border-left: 4px solid $u-type-primary;
  499. height: 32rpx;
  500. left: 0;
  501. top: 39rpx;
  502. }
  503. .u-tab-view {
  504. height: 100%;
  505. }
  506. .right-box {
  507. background-color: rgb(250, 250, 250);
  508. }
  509. .page-view {
  510. padding: 16rpx;
  511. }
  512. .class-item {
  513. margin-bottom: 30rpx;
  514. background-color: #fff;
  515. padding: 16rpx;
  516. border-radius: 8rpx;
  517. }
  518. .item-title {
  519. font-size: 26rpx;
  520. color: $u-main-color;
  521. font-weight: bold;
  522. }
  523. .item-container {
  524. display: flex;
  525. flex-wrap: wrap;
  526. .thumb-box {
  527. width: 100%;
  528. display: flex;
  529. padding: 20rpx 0;
  530. border-bottom: 1rpx solid #EEEEEE;
  531. .item-menu-image {
  532. width: 160rpx;
  533. height: 160rpx;
  534. margin-right: 20rpx;
  535. border-radius: 8rpx;
  536. }
  537. .item-content {
  538. width: calc(100% - 180rpx);
  539. }
  540. .name {
  541. font-size: $font-size-base;
  542. margin-bottom: 10rpx;
  543. }
  544. .tips {
  545. width: 100%;
  546. height: 40rpx;
  547. display: flex;
  548. justify-content: space-between;
  549. line-height: 40rpx;
  550. white-space: nowrap;
  551. font-size: $font-size-sm;
  552. color: $text-color-assist;
  553. margin-bottom: 10rpx;
  554. }
  555. .price_and_action {
  556. width: 100%;
  557. display: flex;
  558. align-items: center;
  559. margin-top: 30rpx;
  560. .price {
  561. font-size: $font-size-base;
  562. font-weight: 600;
  563. }
  564. .btn-group {
  565. display: flex;
  566. justify-content: space-between;
  567. align-items: center;
  568. position: relative;
  569. .btn {
  570. background-color: $base-color;
  571. padding: 0 20rpx;
  572. box-sizing: border-box;
  573. font-size: $font-size-sm;
  574. height: 44rpx;
  575. line-height: 44rpx;
  576. &.property_btn {
  577. border-radius: 24rpx;
  578. }
  579. &.add_btn,
  580. &.reduce_btn {
  581. color: #FFFFFF;
  582. border: $base-color;
  583. padding: 0;
  584. width: 44rpx;
  585. border-radius: 44rpx;
  586. }
  587. }
  588. .dot {
  589. position: absolute;
  590. background-color: #ffffff;
  591. border: 1px solid $color-primary;
  592. color: $color-primary;
  593. font-size: $font-size-sm;
  594. width: 36rpx;
  595. height: 36rpx;
  596. line-height: 36rpx;
  597. text-align: center;
  598. border-radius: 100%;
  599. right: -12rpx;
  600. top: -10rpx;
  601. }
  602. .number {
  603. width: 44rpx;
  604. height: 44rpx;
  605. line-height: 44rpx;
  606. text-align: center;
  607. }
  608. }
  609. }
  610. }
  611. .thumb-box:last-child {
  612. border: none;
  613. }
  614. }
  615. </style>