index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  1. <template>
  2. <view class="goods-list">
  3. <!-- #ifdef MP || APP-PLUS -->
  4. <view class="accountTitle">
  5. <view :style="{ height: getHeight.barTop + 'px' }"></view>
  6. <view
  7. class="sysTitle acea-row row-center-wrapper"
  8. :style="{ height: getHeight.barHeight + 'px' }"
  9. >
  10. <view>商品管理</view>
  11. <text class="iconfont icon-ic_leftarrow" @click="goarrow"></text>
  12. </view>
  13. </view>
  14. <view
  15. :style="{ height: getHeight.barTop + getHeight.barHeight + 'px' }"
  16. ></view>
  17. <!-- #endif -->
  18. <view class="searchCon acea-row row-between-wrapper">
  19. <view class="search acea-row row-middle">
  20. <text class="iconfont icon-ic_search"></text>
  21. <input
  22. class="inputs"
  23. placeholder="请输入商品名称/ID"
  24. placeholder-class="placeholder"
  25. confirm-type="search"
  26. name="search"
  27. v-model="keyword"
  28. @confirm="searchSubmit"
  29. />
  30. </view>
  31. <view @click="manageTap" v-if="administer">取消</view>
  32. <view class="edit acea-row row-center-wrapper" @click="manageTap" v-else>
  33. <text class="iconfont icon-ic_batch"></text>
  34. </view>
  35. </view>
  36. <view class="nav acea-row row-middle">
  37. <view
  38. class="item"
  39. :class="current == index ? 'on' : ''"
  40. v-for="(item, index) in navList"
  41. :key="index"
  42. @click="navTap(item, index)"
  43. >
  44. {{ item.name }}
  45. <image src="../static/adorn.png" v-if="current == index" />
  46. </view>
  47. </view>
  48. <view class="list" v-if="goodsList.length">
  49. <checkbox-group @change="checkboxChange">
  50. <view
  51. class="acea-row row-middle"
  52. v-for="(item, index) in goodsList"
  53. :key="index"
  54. >
  55. <!-- #ifndef MP -->
  56. <checkbox
  57. class="checkbox"
  58. v-if="administer"
  59. :value="item.id.toString()"
  60. :checked="item.checked"
  61. color="#ffffff"
  62. backgroundColor="#ffffff"
  63. activeBackgroundColor="#2A7EFB"
  64. activeBorderColor="#2A7EFB"
  65. />
  66. <!-- #endif -->
  67. <!-- #ifdef MP -->
  68. <checkbox
  69. class="checkbox"
  70. v-if="administer"
  71. :value="item.id"
  72. :checked="item.checked"
  73. color="#ffffff"
  74. />
  75. <!-- #endif -->
  76. <view class="item">
  77. <view class="top acea-row row-center-wrapper" @tap="goDetail(item)">
  78. <view class="pictrue">
  79. <image :src="item.image"></image>
  80. </view>
  81. <view class="text">
  82. <view class="name line1">{{ item.store_name }}</view>
  83. <view class="info">
  84. <text>销量: {{ item.sales }}</text>
  85. <text>库存: {{ item.stock }}</text>
  86. </view>
  87. <baseMoney
  88. :money="item.price"
  89. symbolSize="20"
  90. integerSize="32"
  91. decimalSize="20"
  92. color="#FF7E00"
  93. ></baseMoney>
  94. </view>
  95. </view>
  96. <view class="bottom acea-row row-right" v-if="!administer">
  97. <view
  98. class="bnt acea-row row-center-wrapper"
  99. :class="item.is_show ? '' : 'up'"
  100. @click="setShow(item, 0)"
  101. >{{ item.is_show ? "下架" : "上架" }}</view
  102. >
  103. <view
  104. class="bnt on acea-row row-center-wrapper"
  105. @tap="openDrawer(item)"
  106. >编辑</view
  107. >
  108. </view>
  109. </view>
  110. </view>
  111. </checkbox-group>
  112. </view>
  113. <view class="empty-box" v-if="goodsList.length == 0 && !loading">
  114. <emptyPage
  115. title="暂无商品~"
  116. src="/statics/images/empty-box.gif"
  117. ></emptyPage>
  118. </view>
  119. <Loading :loaded="loadend" :loading="loading"></Loading>
  120. <view class="footerH"></view>
  121. <view class="footer acea-row row-between-wrapper" v-if="administer">
  122. <checkbox-group @change="checkboxAllChange">
  123. <checkbox
  124. value="all"
  125. :checked="isAllSelect"
  126. color="#ffffff"
  127. backgroundColor="#ffffff"
  128. activeBackgroundColor="#2A7EFB"
  129. activeBorderColor="#2A7EFB"
  130. />
  131. <text class="checkAll">全选({{ getIds().length }})</text>
  132. </checkbox-group>
  133. <view class="acea-row row-middle">
  134. <view class="bnt acea-row row-center-wrapper" @click="editLabels"
  135. >添加标签</view
  136. >
  137. <view class="bnt acea-row row-center-wrapper" @click="editClass"
  138. >修改分类</view
  139. >
  140. <view
  141. class="bnt acea-row row-center-wrapper"
  142. v-if="type == 1"
  143. @click="setShow('', 1, 0)"
  144. >下架</view
  145. >
  146. <view
  147. class="bnt acea-row row-center-wrapper"
  148. v-else-if="type == 2"
  149. @click="setShow('', 1, 1)"
  150. >上架</view
  151. >
  152. </view>
  153. </view>
  154. <footer-page></footer-page>
  155. <base-drawer
  156. mode="bottom"
  157. :visible="visible"
  158. background-color="transparent"
  159. mask
  160. maskClosable
  161. @close="closeDrawer"
  162. >
  163. <view class="edit-list rd-t-40rpx">
  164. <view
  165. class="item"
  166. :class="{ disabled: index === 0 && goodsInfo.virtual_type != 0 }"
  167. v-for="(item, index) in editList"
  168. :key="index"
  169. @tap="editInfo(index)"
  170. >{{ item.name }}</view
  171. >
  172. </view>
  173. </base-drawer>
  174. <edit-price
  175. :visible="visiblePrice"
  176. :goodsInfo="goodsInfo"
  177. @closeDrawer="priceCloseDrawer"
  178. @successChange="successChange"
  179. ></edit-price>
  180. <edit-lable
  181. ref="lable"
  182. :visible="visibleLable"
  183. @closeDrawer="lableCloseDrawer"
  184. @successChange="successChange"
  185. ></edit-lable>
  186. <classify
  187. ref="classify"
  188. :visible="visibleClass"
  189. @closeDrawer="classCloseDrawer"
  190. @successChange="successChange"
  191. ></classify>
  192. </view>
  193. </template>
  194. <script>
  195. import Loading from "@/components/Loading/index";
  196. import emptyPage from "@/components/emptyPage.vue";
  197. import baseDrawer from "@/components/tuiDrawer/tui-drawer.vue";
  198. import footerPage from "../components/footerPage/index.vue";
  199. import editPrice from "./components/editPrice/index.vue";
  200. import editLable from "./components/label/index.vue";
  201. import classify from "./components/classify/index.vue";
  202. import { goShopDetail } from "@/libs/order.js";
  203. import { adminProductList, productSetShow } from "@/api/admin";
  204. export default {
  205. components: {
  206. editPrice,
  207. editLable,
  208. classify,
  209. footerPage,
  210. emptyPage,
  211. Loading,
  212. baseDrawer,
  213. },
  214. data() {
  215. return {
  216. getHeight: this.$util.getWXStatusHeight(),
  217. navList: [
  218. {
  219. name: "全部",
  220. type: "",
  221. },
  222. {
  223. name: "出售中",
  224. type: 1,
  225. },
  226. {
  227. name: "仓库中",
  228. type: 2,
  229. },
  230. {
  231. name: "已售罄",
  232. type: 4,
  233. },
  234. {
  235. name: "库存警告",
  236. type: 5,
  237. },
  238. ],
  239. editList: [
  240. {
  241. name: "修改价格/库存",
  242. },
  243. {
  244. name: "商品分类",
  245. },
  246. {
  247. name: "商品标签",
  248. },
  249. ],
  250. current: 0,
  251. administer: 0,
  252. isAllSelect: false,
  253. goodsList: [],
  254. goodsInfo: {},
  255. visible: false,
  256. visiblePrice: false, //价格库存是否显示
  257. visibleLable: false, //标签是否显示
  258. loadTitle: "加载更多",
  259. loading: false,
  260. loadend: false,
  261. limit: 20,
  262. page: 1,
  263. keyword: "", //搜索字段
  264. type: "", //商品状态
  265. visibleClass: false,
  266. };
  267. },
  268. onShow() {},
  269. onLoad(option) {
  270. this.type = option.type || "";
  271. this.keyword = option.keyword || "";
  272. for (let i = 0; i < this.navList.length; i++) {
  273. if (this.navList[i].type == this.type) {
  274. this.current = i;
  275. break;
  276. }
  277. }
  278. this.productList();
  279. },
  280. methods: {
  281. // 去详情页
  282. goDetail(item) {
  283. if (!item.is_show) {
  284. this.$util.Tips({
  285. title: "商品未上架",
  286. });
  287. return;
  288. }
  289. goShopDetail(item, this.$store.state.app.uid).catch((res) => {
  290. uni.navigateTo({
  291. url: `/pages/goods_details/index?id=${item.id}`,
  292. });
  293. });
  294. },
  295. goarrow() {
  296. uni.navigateBack();
  297. },
  298. editClass() {
  299. if (!this.getIds().length) {
  300. this.$util.Tips({
  301. title: "请选择商品",
  302. });
  303. return;
  304. }
  305. this.$refs.classify.category(this.getIds(), "");
  306. let that = this;
  307. setTimeout(function () {
  308. that.visibleClass = true;
  309. }, 200);
  310. },
  311. classCloseDrawer() {
  312. this.visibleClass = false;
  313. },
  314. successChange() {
  315. this.visibleClass = false;
  316. this.visibleLable = false;
  317. this.visiblePrice = false;
  318. this.init();
  319. },
  320. //批量编辑标签
  321. editLabels() {
  322. if (!this.getIds().length) {
  323. this.$util.Tips({
  324. title: "请选择商品",
  325. });
  326. return;
  327. }
  328. this.visibleLable = true;
  329. this.$refs.lable.productLabel({}, 1, this.getIds());
  330. },
  331. //批量获取id集合
  332. getIds() {
  333. let ids = [];
  334. this.goodsList.forEach((item) => {
  335. if (item.checked) {
  336. ids.push(item.id);
  337. }
  338. });
  339. ids.slice(0, 100);
  340. return ids;
  341. },
  342. setShow(item, num, type) {
  343. let data = {};
  344. if (num) {
  345. if (!this.getIds().length) {
  346. this.$util.Tips({
  347. title: "请选择商品",
  348. });
  349. return;
  350. }
  351. data = {
  352. id: this.getIds(),
  353. is_show: type,
  354. };
  355. } else {
  356. data = {
  357. id: item.id,
  358. is_show: item.is_show ? 0 : 1,
  359. };
  360. }
  361. productSetShow(data).then((res) => {
  362. this.$util.Tips({
  363. title: res.msg,
  364. });
  365. let i = this.goodsList.findIndex(val=> val.id == item.id);
  366. this.goodsList.splice(i ,1);
  367. // item.is_show = item.is_show ? 0 : 1
  368. })
  369. .catch((err) => {
  370. this.$util.Tips({
  371. title: err,
  372. });
  373. });
  374. },
  375. init() {
  376. this.goodsList = [];
  377. this.page = 1;
  378. this.loadend = false;
  379. this.loading = false;
  380. this.productList();
  381. },
  382. searchSubmit() {
  383. this.init();
  384. },
  385. productList() {
  386. let that = this;
  387. if (this.loading) return;
  388. if (this.loadend) return;
  389. that.loading = true;
  390. that.loadTitle = "";
  391. adminProductList({
  392. page: that.page,
  393. limit: that.limit,
  394. store_name: that.keyword,
  395. type: that.type,
  396. })
  397. .then((res) => {
  398. let goodsList = res.data.list;
  399. goodsList.forEach((item) => {
  400. item.checked = false;
  401. });
  402. this.isAllSelect = false;
  403. let loadend = goodsList.length < that.limit;
  404. that.goodsList = that.$util.SplitArray(goodsList, that.goodsList);
  405. that.$set(that, "goodsList", that.goodsList);
  406. that.loadend = loadend;
  407. that.loadTitle = loadend ? "没有更多内容啦~" : "加载更多";
  408. that.page = that.page + 1;
  409. that.loading = false;
  410. })
  411. .catch((err) => {
  412. that.loading = false;
  413. that.loadTitle = "加载更多";
  414. });
  415. },
  416. editInfo(index) {
  417. switch (index) {
  418. case 0:
  419. if (this.goodsInfo.virtual_type != 0) {
  420. this.$util.Tips({ title: '仅普通商品可在此处修改价格/库存' });
  421. return;
  422. }
  423. this.visible = false;
  424. if (this.goodsInfo.spec_type) {
  425. uni.navigateTo({
  426. url: "/pages/admin/goods/specs?id=" + this.goodsInfo.id,
  427. });
  428. } else {
  429. this.visiblePrice = true;
  430. }
  431. break;
  432. case 1:
  433. this.visible = false;
  434. this.visibleClass = true;
  435. break;
  436. case 2:
  437. this.visibleLable = true;
  438. this.visible = false;
  439. break;
  440. }
  441. },
  442. lableCloseDrawer() {
  443. this.visibleLable = false;
  444. },
  445. priceCloseDrawer() {
  446. this.visiblePrice = false;
  447. },
  448. openDrawer(item) {
  449. this.visible = true;
  450. this.goodsInfo = JSON.parse(JSON.stringify(item));
  451. this.$refs.lable.productLabel(this.goodsInfo, 0, []);
  452. this.$refs.classify.category(item.id, item.cate_id);
  453. },
  454. closeDrawer() {
  455. this.visible = false;
  456. },
  457. manageTap() {
  458. this.administer = !this.administer;
  459. },
  460. navTap(item, index) {
  461. this.current = index;
  462. if (this.type != item.type) {
  463. this.type = item.type;
  464. this.init();
  465. }
  466. },
  467. checkboxChange(event) {
  468. let idList = event.detail.value;
  469. this.goodsList.forEach((item) => {
  470. if (idList.indexOf(item.id + "") !== -1) {
  471. item.checked = true;
  472. } else {
  473. item.checked = false;
  474. }
  475. });
  476. if (idList.length == this.goodsList.length) {
  477. this.isAllSelect = true;
  478. } else {
  479. this.isAllSelect = false;
  480. }
  481. },
  482. forGoods(val) {
  483. let that = this;
  484. if (!that.goodsList.length) return;
  485. that.goodsList.forEach((item) => {
  486. if (val) {
  487. item.checked = true;
  488. } else {
  489. item.checked = false;
  490. }
  491. });
  492. },
  493. checkboxAllChange(event) {
  494. let value = event.detail.value;
  495. if (value.length) {
  496. if (this.goodsList.length > 100) {
  497. this.$util.Tips({
  498. title: "每次最多只提交100条数据",
  499. });
  500. }
  501. this.isAllSelect = true;
  502. this.forGoods(1);
  503. } else {
  504. this.isAllSelect = false;
  505. this.forGoods(0);
  506. }
  507. },
  508. },
  509. onReachBottom() {
  510. this.productList();
  511. },
  512. };
  513. </script>
  514. <style lang="scss" scoped>
  515. ::v-deep checkbox .wx-checkbox-input.wx-checkbox-input-checked {
  516. border: 1px solid $primary-admin !important;
  517. background-color: $primary-admin !important;
  518. }
  519. ::v-deep uni-checkbox .uni-checkbox-input {
  520. margin-top: -4rpx;
  521. }
  522. ::v-deep checkbox:not([disabled]) .uni-checkbox-input:hover {
  523. border-color: #d1d1d1 !important;
  524. }
  525. .empty-box {
  526. padding: 0 20rpx;
  527. }
  528. .accountTitle {
  529. background: #f5f5f5;
  530. position: fixed;
  531. left: 0;
  532. top: 0;
  533. width: 100%;
  534. z-index: 99;
  535. padding-bottom: 6rpx;
  536. .sysTitle {
  537. width: 100%;
  538. position: relative;
  539. font-weight: 500;
  540. color: #333333;
  541. font-size: 30rpx;
  542. .iconfont {
  543. position: absolute;
  544. font-size: 42rpx;
  545. left: 20rpx;
  546. width: 60rpx;
  547. font-weight: 500;
  548. }
  549. }
  550. }
  551. .goods-list {
  552. padding-top: 20rpx;
  553. .searchCon {
  554. padding: 0 20rpx;
  555. .search {
  556. width: 618rpx;
  557. height: 72rpx;
  558. background: #ffffff;
  559. border-radius: 50rpx;
  560. padding: 0 34rpx;
  561. .iconfont {
  562. color: #999;
  563. font-size: 32rpx;
  564. margin-right: 16rpx;
  565. }
  566. .inputs {
  567. font-size: 28rpx;
  568. width: 100%;
  569. height: 100%;
  570. flex: 1;
  571. }
  572. .placeholder {
  573. color: #ccc;
  574. }
  575. }
  576. .edit {
  577. width: 72rpx;
  578. height: 72rpx;
  579. background: #ffffff;
  580. border-radius: 50%;
  581. .iconfont {
  582. color: #666;
  583. font-size: 36rpx;
  584. }
  585. }
  586. }
  587. .nav {
  588. padding: 20rpx 20rpx 18rpx 34rpx;
  589. position: sticky;
  590. top: 0;
  591. left: 0;
  592. width: 100%;
  593. background-color: #f5f5f5;
  594. z-index: 9;
  595. .item {
  596. font-weight: 400;
  597. font-size: 26rpx;
  598. color: #333333;
  599. padding: 10rpx 0;
  600. position: relative;
  601. & ~ .item {
  602. margin-left: 72rpx;
  603. }
  604. &.on {
  605. font-weight: 500;
  606. color: $primary-admin;
  607. font-size: 30rpx;
  608. font-family: PingFang SC, PingFang SC;
  609. }
  610. image {
  611. width: 14rpx;
  612. height: 14rpx;
  613. display: block;
  614. position: absolute;
  615. bottom: 0;
  616. right: -4rpx;
  617. }
  618. }
  619. }
  620. .list {
  621. padding-bottom: 20rpx;
  622. padding: 0 20rpx 20rpx 20rpx;
  623. ::v-deep uni-checkbox .uni-checkbox-input {
  624. background-color: #f5f5f5;
  625. margin: 0 20rpx 20rpx 0;
  626. }
  627. ::v-deep wx-checkbox .wx-checkbox-input {
  628. background-color: #f5f5f5;
  629. margin: 0 20rpx 20rpx 0;
  630. }
  631. .item {
  632. width: 100%;
  633. background-color: #fff;
  634. padding: 24rpx;
  635. box-sizing: border-box;
  636. margin-bottom: 20rpx;
  637. border-radius: 24rpx;
  638. flex: 1;
  639. overflow: hidden;
  640. .top {
  641. .checkbox {
  642. margin-right: 10rpx;
  643. }
  644. .pictrue {
  645. width: 136rpx;
  646. height: 136rpx;
  647. margin-right: 20rpx;
  648. image {
  649. width: 100%;
  650. height: 100%;
  651. border-radius: 16rpx;
  652. }
  653. }
  654. .text {
  655. flex: 1;
  656. overflow: hidden;
  657. .name {
  658. font-size: 28rpx;
  659. font-weight: 400;
  660. color: #333333;
  661. }
  662. .info {
  663. font-size: 24rpx;
  664. font-weight: 400;
  665. color: #666666;
  666. margin: 10rpx 0 18rpx 0;
  667. text {
  668. margin-right: 40rpx;
  669. }
  670. }
  671. }
  672. }
  673. .bottom {
  674. margin-top: 26rpx;
  675. .bnt {
  676. width: 144rpx;
  677. height: 56rpx;
  678. border: 1px solid #ccc;
  679. font-size: 24rpx;
  680. font-family: PingFang SC, PingFang SC;
  681. font-weight: 400;
  682. color: #333333;
  683. border-radius: 50rpx;
  684. & ~ .bnt {
  685. margin-left: 16rpx;
  686. }
  687. &.on {
  688. border-color: $primary-admin;
  689. background-color: $primary-admin;
  690. color: #fff;
  691. }
  692. &.up {
  693. border-color: #ff7e00;
  694. color: #ff7e00;
  695. }
  696. }
  697. }
  698. }
  699. }
  700. .footerH {
  701. height: 110rpx;
  702. height: calc(110rpx + constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
  703. height: calc(110rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
  704. }
  705. .footer {
  706. box-sizing: border-box;
  707. padding: 0 32rpx;
  708. width: 100%;
  709. height: 96rpx;
  710. background-color: #fff;
  711. position: fixed;
  712. bottom: 0;
  713. z-index: 30;
  714. height: calc(96rpx + constant(safe-area-inset-bottom)); ///兼容 IOS<11.2/
  715. height: calc(96rpx + env(safe-area-inset-bottom)); ///兼容 IOS>11.2/
  716. padding-bottom: constant(safe-area-inset-bottom); ///兼容 IOS<11.2/
  717. padding-bottom: env(safe-area-inset-bottom); ///兼容 IOS>11.2/
  718. width: 100%;
  719. left: 0;
  720. .bnt {
  721. width: 160rpx;
  722. height: 64rpx;
  723. border-radius: 50rpx;
  724. border: 1rpx solid $primary-admin;
  725. color: $primary-admin;
  726. font-size: 24rpx;
  727. font-family: PingFang SC, PingFang SC;
  728. font-weight: 500;
  729. & ~ .bnt {
  730. margin-left: 16rpx;
  731. }
  732. }
  733. }
  734. .edit-list {
  735. background-color: #fff;
  736. padding: 45rpx 34rpx;
  737. .item {
  738. font-family: PingFang SC, PingFang SC;
  739. font-weight: 400;
  740. color: #333333;
  741. font-size: 32rpx;
  742. text-align: center;
  743. height: 106rpx;
  744. line-height: 106rpx;
  745. &.disabled {
  746. color: #cccccc;
  747. }
  748. }
  749. }
  750. }
  751. </style>