lex 2 years ago
parent
commit
681f54fd08

BIN
src/assets/images/icon_del.png


+ 5 - 1
src/components/MRefund.vue

@@ -54,7 +54,7 @@
     <Mcoupon
       class="Mcoupon"
       ref="Mcoupon"
-      :showCoupon="true"
+      :showCoupon="showCoupon"
       :showBalance="balance > 0"
       :balance="balance"
       @onClickCheckbox="onClickCheckbox"
@@ -192,6 +192,10 @@ export default {
     disCountList: {
       type: Array,
     },
+    showCoupon: {
+      type: Boolean,
+      default: true,
+    },
     moneyList: {
       type: Array,
       default() {

+ 3 - 1
src/router/index.js

@@ -4,6 +4,7 @@ import TeacherRouter from "./teacherRouter";
 import AppRouter from "./appRouter";
 import AuditionRouter from "./auditionRouter";
 import ServiceRouter from "./serviceRouter";
+import ShopRouter from "./shopRouter";
 
 Vue.use(Router);
 
@@ -134,7 +135,8 @@ defaultRouter = defaultRouter
   .concat(TeacherRouter)
   .concat(AppRouter)
   .concat(AuditionRouter)
-  .concat(ServiceRouter);
+  .concat(ServiceRouter)
+  .concat(ShopRouter);
 
 const router = new Router({
   // mode: 'history',

+ 34 - 0
src/router/shopRouter.js

@@ -0,0 +1,34 @@
+const shopRouter = [
+  {
+    path: "/shopGoodsSale",
+    name: "shopGoodsSale",
+    component: () =>
+      import(/* webpackChunkName:'GoodsSale'*/ "@/views/shopMall/GoodsSale.vue"),
+    meta: {
+      descrition: "商品销售",
+      weight: 2,
+    },
+  },
+  {
+    path: "/shopGoodsOrder",
+    name: "shopGoodsOrder",
+    component: () =>
+      import(/* webpackChunkName:'GoodsOrder'*/ "@/views/shopMall/GoodsOrder.vue"),
+    meta: {
+      descrition: "商品订单",
+      weight: 2,
+    },
+  },
+  {
+    path: "/shopGoodsOrderDetail",
+    name: "shopGoodsOrderDetail",
+    component: () =>
+      import(/* webpackChunkName:'GoodsOrderDetail'*/ "@/views/shopMall/GoodsOrderDetail.vue"),
+    meta: {
+      descrition: "订单详情",
+      weight: 2,
+    },
+  },
+];
+
+export default shopRouter;

+ 1 - 4
src/views/service/GoodsSale.vue

@@ -921,10 +921,7 @@ export default {
     window.removeEventListener("hashchange", this.onHash, false);
     this.$toast.clear();
     this.qrCodeStatus = false;
-  },
-  computed: {
-    groupAllPrice() {},
-  },
+  }
 };
 </script>
 <style lang="less" scoped>

+ 7 - 6
src/views/service/ServiceStudent.vue

@@ -1,12 +1,14 @@
 <template>
   <div style="min-height: 100vh">
     <van-sticky>
-      <m-header :backUrl="backUrl" :isFixed="false">
+      <m-header :backUrl="backUrl" :isFixed="false" :appHide="true">
         <template slot="right">
           <div
             @click="
               () => {
-                $router.push('/goodsOrder');
+                const route =
+                  type == 'goods' ? '/shopGoodsOrder' : '/goodsOrder';
+                $router.push(route);
               }
             "
           >
@@ -125,16 +127,16 @@ export default {
   methods: {
     onSelect(item) {
       // 商品销售
+      const route = this.type == "goods" ? "/shopGoodsSale" : "/goodsSale";
+      // 商品销售
       this.$router.push({
-        path: "/goodsSale",
+        path: route,
         query: {
           studentId: item.userId,
           studentName: item.name,
           organId: item.organId,
         },
       });
-      // this.radioSelect = item.userId;
-      // this.radioSelectOrganId = item.organId;
     },
     onSubmit() {
       if (!this.radioSelect) {
@@ -148,7 +150,6 @@ export default {
           studentName = item.name;
         }
       });
-      // 商品销售
       this.$router.push({
         path: "/goodsSale",
         query: {

+ 504 - 0
src/views/shopMall/GoodsOrder.vue

@@ -0,0 +1,504 @@
+<template>
+  <div class="goodsOrder">
+    <m-header :backUrl="backUrl" />
+    <van-tabs
+      color="#01C1B5"
+      title-active-color="#000"
+      @change="onTabChange"
+      title-inactive-color="#808080"
+      v-model="activeTab"
+    >
+      <van-tab title="待支付" name="ING">
+        <search
+          @onSearch="onSearch"
+          placeholder="请输入学生姓名或手机号"
+          style="margin-bottom: 0.15rem"
+        />
+        <van-list
+          v-model="loading"
+          v-if="dataShow"
+          key="ing"
+          :finished="finished"
+          :immediate-check="false"
+          finished-text=" "
+          @load="getGoodsList()"
+        >
+          <van-cell-group
+            class="order-section"
+            :border="false"
+            v-for="(list, index) in dataList"
+            :key="index"
+            @click="onGoodDetail(list)"
+          >
+            <van-cell title-class="order-title" value-class="order-status">
+              <template #title>学生姓名:{{ list.userName }}</template>
+              <template>待支付</template>
+            </van-cell>
+            <van-cell
+              v-for="(item, i) in list.goodsJson"
+              :border="i == list.goodsJson.length - 1 ? true : false"
+              :key="i"
+              :class="[
+                i != list.goodsJson.length - 1 ? 'input-bottom' : null,
+                'input-cell',
+              ]"
+              class="input-cell"
+              :center="true"
+            >
+              <template #icon>
+                <van-image :src="item.productPic" class="logo">
+                  <template v-slot:loading>
+                    <van-loading type="spinner" size="20" />
+                  </template>
+                </van-image>
+              </template>
+              <template #title>
+                {{ item.productName }}
+                <p class="money">¥{{ item.productPrice | moneyFormat }}</p>
+              </template>
+              <template #default> x {{ item.productQuantity }} </template>
+            </van-cell>
+            <van-cell
+              title-class="order-money"
+              title-style="flex: auto 1"
+              style="align-items: center"
+              class="van-hairline--top"
+            >
+              <template #title>
+                订单金额:<span>¥{{ list.totalAmount | moneyFormat }}</span>
+              </template>
+              <template>
+                <van-button
+                  size="small"
+                  round
+                  class="van-small__btn"
+                  @click.stop="onRePay(list)"
+                  type="default"
+                  >继续支付</van-button
+                >
+              </template>
+            </van-cell>
+          </van-cell-group>
+        </van-list>
+        <m-empty v-else key="ing" msg="暂无订单" />
+      </van-tab>
+      <van-tab title="已支付" name="SUCCESS">
+        <search
+          @onSearch="onSearch2"
+          placeholder="请输入学生姓名或手机号"
+          style="margin-bottom: 0.15rem"
+        />
+        <van-list
+          v-model="loading2"
+          v-if="dataShow2"
+          key="success"
+          :immediate-check="false"
+          :finished="finished2"
+          finished-text=" "
+          @load="getGoodsList2()"
+        >
+          <van-cell-group
+            class="order-section"
+            :border="false"
+            v-for="(list, index) in dataList2"
+            :key="index"
+            @click="onGoodDetail(list)"
+          >
+            <van-cell
+              title-class="order-title"
+              value-class="order-status success"
+            >
+              <template #title
+                >学生姓名:{{ list.userName }}
+                <span
+                  style="padding-left: 0.1rem"
+                  v-if="list.authorUser != operationId"
+                  >学员自建</span
+                ></template
+              >
+              <template>支付成功</template>
+            </van-cell>
+            <van-cell
+              v-for="(item, i) in list.goodsJson"
+              :border="i == list.goodsJson.length - 1 ? true : false"
+              :key="i"
+              :class="[
+                i != list.goodsJson.length - 1 ? 'input-bottom' : null,
+                'input-cell',
+              ]"
+              :center="true"
+            >
+              <template #icon>
+                <van-image :src="item.productPic" class="logo">
+                  <template v-slot:loading>
+                    <van-loading type="spinner" size="20" />
+                  </template>
+                </van-image>
+              </template>
+              <template #title>
+                {{ item.productName }}
+                <p class="money">¥{{ item.productPrice | moneyFormat }}</p>
+              </template>
+              <template #default> x {{ item.productQuantity }} </template>
+            </van-cell>
+            <van-cell
+              title-class="order-money"
+              class="van-hairline--top order-payment"
+              title=" "
+            >
+              <template #title>
+                订单金额:<span>¥{{ list.totalAmount | moneyFormat }}</span>
+              </template>
+              <template>
+                <div>
+                  <div
+                    class="order-money-get"
+                    v-if="list.receiveStatus == 'NO_RECEIVE'"
+                  >
+                    未确认
+                  </div>
+                  <div v-if="list.receiveStatus == 'AUTO_RECEIVE'">
+                    自动确认收货
+                  </div>
+                  <div v-if="list.receiveStatus == 'MANUAL_RECEIVE'">
+                    手动确认收货
+                  </div>
+                </div>
+              </template>
+            </van-cell>
+          </van-cell-group>
+        </van-list>
+        <m-empty v-else key="success" msg="暂无订单" />
+      </van-tab>
+    </van-tabs>
+    <m-payment
+      :closeStatus="isStatus"
+      :amount="payMoney"
+      :payment="payment"
+      :showCoupon="false"
+      @onChangeStatus="onChangeStatus"
+    />
+  </div>
+</template>
+
+<script>
+/* eslint-disable */
+import { queryStudentGoodsOrders, addGoodsSellOrder } from "../service/api";
+import MHeader from "@/components/MHeader";
+import MEmpty from "@/components/MEmpty";
+import MPayment from "@/components/MPayment";
+import Search from "@/components/Search";
+import { mallGenerateOrder, mallCartAddAll } from "./api";
+import setLoading from "@/common/loading";
+export default {
+  name: "goodsOrder",
+  components: {
+    MHeader,
+    MEmpty,
+    Search,
+    MPayment,
+  },
+  data() {
+    let query = this.$route.query;
+    let operationId = localStorage.getItem("operationId");
+    return {
+      operationId: operationId,
+      activeTab: query.activeTab ? query.activeTab : "ING",
+      isStatus: false,
+      payment: {}, // 支付对象
+      payMoney: 0,
+      dataList: [],
+      dataShow: true,
+      loading: false,
+      finished: false,
+      params: {
+        search: null,
+        status: "ING",
+        goodsType: "MALL",
+        page: 1,
+        rows: 10,
+      },
+      dataList2: [],
+      dataShow2: true,
+      loading2: false,
+      finished2: false,
+      params2: {
+        search: null,
+        status: "SUCCESS",
+        goodsType: "MALL",
+        page: 1,
+        rows: 10,
+      },
+      backUrl: {
+        status: true,
+        path: "/home",
+      },
+    };
+  },
+  async mounted() {
+    // 判断是否在app里面
+    localStorage.removeItem("shopOrderGoodsDetail");
+    console.log("join");
+    await this.onTabChange();
+  },
+  methods: {
+    onTabChange() {
+      if (this.activeTab == "ING") {
+        this.getGoodsList();
+      } else if (this.activeTab == "SUCCESS") {
+        this.getGoodsList2();
+      }
+    },
+    onGoodDetail(item) {
+      localStorage.setItem("shopOrderGoodsDetail", JSON.stringify(item));
+      this.$router.push({
+        path: "/shopGoodsOrderDetail",
+        query: {
+          activeTab: this.activeTab,
+        },
+      });
+    },
+    onSearch(value) {
+      this.params.search = value;
+      this.params.page = 1;
+      this.dataList = [];
+      this.dataShow = true;
+      this.loading = true;
+      this.finished = false;
+      this.getGoodsList();
+    },
+    async getGoodsList() {
+      let params = this.params;
+      await queryStudentGoodsOrders(params).then((result) => {
+        this.loading = false;
+        if (result.code == 200) {
+          let rows = result.data.rows;
+          if (result.data.pageNo == 1 && this.dataList.length > 0) {
+            return;
+          }
+          rows.forEach((item) => {
+            console.log(JSON.parse(item.goodsJson));
+            const temp = JSON.parse(item.goodsJson);
+            item.goodsJson = item.goodsJson ? temp.orderItemList : [];
+          });
+          this.dataList.push(...rows);
+          if (params.page >= Math.ceil(result.data.total / params.rows)) {
+            this.finished = true;
+          }
+          this.params.page++;
+
+          console.log(this.dataList, "dataList");
+        } else {
+          this.finished = true;
+        }
+        if (this.dataList.length <= 0) {
+          this.dataShow = false;
+        }
+      });
+    },
+    onSearch2(value) {
+      this.params2.search = value;
+      this.params2.page = 1;
+      this.dataList2 = [];
+      this.dataShow2 = true;
+      this.loading2 = true;
+      this.finished2 = false;
+      this.getGoodsList2();
+    },
+    async getGoodsList2() {
+      let params = this.params2;
+      await queryStudentGoodsOrders(params).then((result) => {
+        this.loading2 = false;
+        if (result.code == 200) {
+          let rows = result.data.rows;
+          rows.forEach((item) => {
+            const temp = JSON.parse(item.goodsJson);
+            item.goodsJson = item.goodsJson ? temp.orderItemList : [];
+          });
+          if (result.data.pageNo == 1 && this.dataList2.length > 0) {
+            return;
+          }
+          this.dataList2.push(...rows);
+          if (params.page >= Math.ceil(result.data.total / params.rows)) {
+            this.finished2 = true;
+          }
+          this.params2.page++;
+        } else {
+          this.finished2 = true;
+        }
+        if (this.dataList2.length <= 0) {
+          this.dataShow2 = false;
+        }
+      });
+    },
+    async onChangeStatus(val) {
+      this.isStatus = val;
+      this.dataList = [];
+      this.dataShow = true;
+      this.loading = true;
+      this.finished = false;
+      this.params = {
+        status: "ING",
+        goodsType: "MALL",
+        page: 1,
+        rows: 10,
+      };
+      this.getGoodsList();
+    },
+    onRePay(item) {
+      // 是否继续支付
+      this.$dialog
+        .confirm({
+          message: "是否继续支付该订单?",
+          confirmButtonColor: "#269a93",
+          cancelButtonText: "取消",
+          confirmButtonText: "继续支付",
+        })
+        .then(() => {
+          // this.buy(item);
+          this.afterPayMent(item);
+        })
+        .catch(() => {
+          this.$dialog.close();
+        });
+    },
+    async afterPayMent(item) {
+      try {
+        const body = {
+          orderNo: item.orderNo,
+          userId: item.userId,
+        };
+        const res = await mallGenerateOrder(body);
+        if (res.data.orderType == "success") {
+          this.$toast(res.msg);
+          this.$router.push({
+            path: "/paymentResult",
+            query: {
+              type: "on",
+              isBack: "off",
+              groupType: "GOODS_SELL",
+            },
+          });
+        } else {
+          // callBack && callBack(res);
+          this.result = res.data;
+          this.onSubmit();
+        }
+      } catch {
+        //
+      }
+    },
+    onSubmit() {
+      // submit 提交
+      let result = this.result;
+      if (result.type == "YQPAY") {
+        let f = result.payMap;
+        document.querySelector("#onSubmit").action = f.host;
+        document.querySelector("#apiContent").value = f.apiContent;
+        document.querySelector("#merNo").value = f.merNo;
+        document.querySelector("#notifyUrl").value = f.notifyUrl;
+        document.querySelector("#sign").value = f.sign;
+        document.querySelector("#signType").value = f.signType;
+        document.querySelector("#timestamp").value = f.timestamp;
+        document.querySelector("#version").value = f.version;
+        document.querySelector("#onSubmit").submit();
+      } else if (result.type == "UNIONPAY") {
+        localStorage.setItem("payInfo", JSON.stringify(result));
+        this.$router.push({
+          path: "/alipay",
+          query: {
+            balance: result.totalPrice,
+          },
+        });
+      } else if (result.type == "ADAPAY") {
+        this.payment = result;
+        this.payMoney = result.payMap.amount;
+        // 开始支付窗口
+        this.isStatus = true;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.goodsOrder {
+  min-height: 100vh;
+  overflow: hidden;
+}
+
+.order-section {
+  margin: 0 0.12rem 0.16rem;
+  border-radius: 0.08rem;
+  overflow: hidden;
+}
+/deep/.van-tabs--line .van-tabs__wrap {
+  height: 0.44rem;
+}
+
+.order-title {
+  color: #808080;
+  font-size: 0.14rem;
+  flex: auto 1;
+}
+
+.order-status {
+  font-size: 0.14rem;
+  color: #f5222d;
+
+  &.success {
+    color: #01c1b5;
+  }
+}
+
+.van-small__btn {
+  font-size: 0.14rem;
+  background-color: #f85043;
+  border-color: #f85043;
+  color: #fff;
+  height: 0.3rem;
+  padding: 0 0.08rem;
+  line-height: 0.28rem;
+}
+
+.order-money {
+  color: #000;
+  font-size: 0.16rem;
+
+  span {
+    color: #ff3535;
+  }
+}
+.order-payment {
+  // margin-top: .15rem;
+}
+
+.input-cell {
+  padding: 0.15rem 0.16rem;
+  &.input-bottom {
+    padding-bottom: 0;
+  }
+  .logo {
+    width: 1rem;
+    height: 1rem;
+    margin-right: 0.15rem;
+    border-radius: 0.05rem;
+  }
+
+  .money {
+    color: #ff3535;
+    font-weight: 600;
+    font-size: 0.18rem;
+  }
+
+  /deep/.van-cell__title {
+    font-size: 0.16rem;
+    color: #000000;
+    flex-basis: 47%;
+  }
+
+  /deep/.van-cell__value {
+    height: 0.2rem;
+  }
+}
+</style>

+ 407 - 0
src/views/shopMall/GoodsOrderDetail.vue

@@ -0,0 +1,407 @@
+<template>
+  <div class="goodsOrder">
+    <m-header :backUrl="backUrl" />
+    <van-cell-group
+      v-if="goodsDetail"
+      class="order-section"
+      style="margin-top: 0.1rem"
+    >
+      <!-- @click="getGoodsDetail(item)" is-link= -->
+      <van-cell
+        v-for="(item, index) in goodsDetail.goodsJson"
+        :key="index"
+        class="input-cell"
+        :center="true"
+      >
+        <template #icon>
+          <van-image :src="item.productPic" class="logo">
+            <template v-slot:loading>
+              <van-loading type="spinner" size="20" />
+            </template>
+          </van-image>
+        </template>
+        <template #title>
+          {{ item.productName }}
+          <p class="money">¥{{ item.productPrice | moneyFormat }}</p>
+        </template>
+        <template #default>
+          <span> x {{ item.productQuantity }}</span>
+        </template>
+      </van-cell>
+    </van-cell-group>
+
+    <van-cell-group v-if="goodsDetail" class="order-section order-info">
+      <p class="order-item">
+        <span>订单总金额</span><span>¥{{ allPrice | moneyFormat }}</span>
+      </p>
+      <p class="order-item">
+        <span>应付金额</span
+        ><span style="color: #ff3535"
+          >¥{{ goodsDetail.totalAmount | moneyFormat }}</span
+        >
+      </p>
+      <p class="order-item">
+        <span>乐器减免</span
+        ><span>¥{{ -goodsDetail.marketAmount | moneyFormat }}</span>
+      </p>
+      <p class="order-item">
+        <span>优惠券</span
+        ><span>¥{{ -goodsDetail.couponRemitFee | moneyFormat }}</span>
+      </p>
+      <p class="order-item">
+        <span>余额支付</span
+        ><span>¥{{ -goodsDetail.balancePaymentAmount | moneyFormat }}</span>
+      </p>
+      <p class="order-item">
+        <span>现金支付</span
+        ><span>¥{{ goodsDetail.actualAmount | moneyFormat }}</span>
+      </p>
+      <p class="order-item">
+        <span>订单状态</span>
+        <span v-if="goodsDetail.status == 'SUCCESS'" style="color: #01c1b5"
+          >支付成功</span
+        >
+        <span v-else style="color: #ff3535">待支付</span>
+      </p>
+      <p class="order-item">
+        <span>学员姓名</span><span>{{ goodsDetail.userName }}</span>
+      </p>
+      <p class="order-item">
+        <span>收货状态</span>
+        <span
+          v-if="goodsDetail.receiveStatus == 'NO_RECEIVE'"
+          style="color: #ff802c"
+          >未确认收货</span
+        >
+        <span v-if="goodsDetail.receiveStatus == 'AUTO_RECEIVE'"
+          >自动确认收货</span
+        >
+        <span v-if="goodsDetail.receiveStatus == 'MANUAL_RECEIVE'"
+          >手动确认收货</span
+        >
+      </p>
+      <p class="order-item">
+        <span>订单号</span><span>{{ goodsDetail.orderNo }}</span>
+      </p>
+      <p class="order-item">
+        <span>交易流水号</span
+        ><span
+          style="max-width: 60%; word-break: break-all; line-height: 1.5"
+          >{{ goodsDetail.transNo }}</span
+        >
+      </p>
+      <p class="order-item">
+        <span>创建时间</span><span>{{ goodsDetail.createTime }}</span>
+      </p>
+      <p class="order-item">
+        <span>付款时间</span><span>{{ goodsDetail.payTime }}</span>
+      </p>
+    </van-cell-group>
+
+    <!-- 商品详情 -->
+    <van-popup
+      v-model="goodsStatus"
+      class="goodsDetail"
+      :lock-scroll="true"
+      position="bottom"
+      :style="{ height: '100%' }"
+    >
+      <m-header :backUrl="backUrl2" name="商品详情" />
+      <van-cell-group>
+        <van-cell class="input-cell" :center="true">
+          <template slot="icon">
+            <van-image
+              :src="popupGoodsDetail.image"
+              class="logo"
+              style="width: 1rem; height: 1rem"
+            >
+              <template v-slot:loading>
+                <van-loading type="spinner" size="20" />
+              </template>
+            </van-image>
+          </template>
+          <template slot="title">
+            <div>{{ popupGoodsDetail.name }}</div>
+            <van-tag plain color="#C2A076" style="margin: 0.04rem 0"
+              >品牌:{{ popupGoodsDetail.brand }}</van-tag
+            >
+            <div class="price-section">
+              <span class="money"
+                ><i>现价:¥</i
+                >{{ popupGoodsDetail.discountPrice | moneyFormat }}</span
+              >
+              <del
+                >原价:¥{{ popupGoodsDetail.marketPrice | moneyFormat }}</del
+              >
+            </div>
+          </template>
+        </van-cell>
+      </van-cell-group>
+
+      <van-cell-group>
+        <van-cell
+          title="品牌"
+          value-class="valueStyle"
+          :value="popupGoodsDetail.brand"
+          :center="true"
+        ></van-cell>
+        <van-cell title="商品类型" value-class="valueStyle" :center="true">
+          {{ popupGoodsDetail.type | shopType }}
+        </van-cell>
+        <van-cell
+          title="商品分类"
+          value-class="valueStyle"
+          :value="popupGoodsDetail.goodsCategoryName"
+          :center="true"
+        ></van-cell>
+        <van-cell
+          title="具体型号"
+          value-class="valueStyle"
+          :value="popupGoodsDetail.specification"
+          :center="true"
+        ></van-cell>
+      </van-cell-group>
+
+      <van-cell-group style="margin-bottom: 0.75rem">
+        <van-cell
+          title="商品详情"
+          style="flex-direction: column"
+          title-class="title-detail"
+          value-class="value-detail"
+          :value="popupGoodsDetail.desc"
+        ></van-cell>
+      </van-cell-group>
+
+      <van-row gutter="10" type="flex" justify="center" class="btn-group">
+        <van-col span="22">
+          <van-button
+            @click="goodsStatus = false"
+            type="primary"
+            color="#01C1B5"
+            plain
+            round
+            block
+            >关闭</van-button
+          >
+        </van-col>
+      </van-row>
+    </van-popup>
+  </div>
+</template>
+
+<script>
+/* eslint-disable */
+import { browser } from "@/common/util";
+import MHeader from "@/components/MHeader";
+import MEmpty from "@/components/MEmpty";
+import setLoading from "@/common/loading";
+import { goodsGet } from "./api";
+export default {
+  name: "goodsOrder",
+  components: { MHeader, MEmpty },
+  data() {
+    let query = this.$route.query;
+    let that = this;
+    return {
+      headerStatus: false,
+      goodsDetail: null,
+      backUrl: {
+        status: true,
+        path: "/shopGoodsOrder?activeTab=" + query.activeTab,
+      },
+      backUrl2: {
+        callBack() {
+          that.goodsStatus = false;
+        },
+      },
+      goodsStatus: false,
+      popupGoodsDetail: {},
+    };
+  },
+  mounted() {
+    // 判断是否在app里面
+    document.title = "订单详情";
+    this.__init();
+  },
+  methods: {
+    __init() {
+      let goodsDetail = localStorage.getItem("shopOrderGoodsDetail");
+      goodsDetail = goodsDetail ? JSON.parse(goodsDetail) : null;
+      this.goodsDetail = goodsDetail;
+    },
+    async getGoodsDetail(item) {
+      // 判断是否请求同一个商品信息
+      if (
+        this.popupGoodsDetail.id &&
+        this.popupGoodsDetail.id == item.goodsId
+      ) {
+        this.goodsStatus = true;
+        return;
+      }
+      setLoading(true);
+      await goodsGet(item.goodsId)
+        .then((res) => {
+          setLoading(false);
+          let result = res.data;
+          if (result.code == 200) {
+            this.popupGoodsDetail = result.data;
+            this.goodsStatus = true;
+          }
+        })
+        .catch(() => {
+          setLoading(false);
+        });
+    },
+  },
+  destroyed() {
+    localStorage.removeItem("shopOrderGoodsDetail");
+  },
+  computed: {
+    allPrice() {
+      let money = 0;
+      if (Array.isArray(this.goodsDetail.goodsJson)) {
+        this.goodsDetail.goodsJson.forEach((goods) => {
+          money += goods.goodsPrice * goods.goodsNum;
+        });
+      }
+
+      return money;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.goodsOrder {
+  min-height: 100vh;
+  overflow: hidden;
+}
+/deep/.van-tab__pane {
+  padding-top: 0.2rem;
+}
+/deep/.van-tab {
+  color: #333;
+  font-size: 0.17rem;
+}
+/deep/.van-tab--active {
+  color: #01c1b5;
+}
+.order-section {
+  margin: 0 0.12rem 0.16rem;
+  border-radius: 0.05rem;
+  overflow: hidden;
+  &.order-info {
+    padding: 0.15rem 0.16rem;
+  }
+  .order-item {
+    font-size: 0.14rem;
+    color: #333;
+    line-height: 2;
+    display: flex;
+    justify-content: space-between;
+  }
+}
+.order-title {
+  color: #808080;
+  font-size: 0.14rem;
+  flex: auto 1;
+}
+.order-status {
+  font-size: 0.14rem;
+  color: #f5222d;
+  &.success {
+    color: #01c1b5;
+  }
+}
+.order-money {
+  color: #000;
+  font-size: 0.16rem;
+  span {
+    color: #ff3535;
+  }
+}
+.input-cell {
+  padding: 0.12rem 0.16rem;
+  .logo {
+    width: 1rem;
+    height: 1rem;
+    margin-right: 0.15rem;
+    border-radius: 0.05rem;
+    overflow: hidden;
+  }
+
+  .price-section {
+    del {
+      font-size: 0.12rem;
+      color: #666666;
+      padding-left: 0.1rem;
+    }
+  }
+
+  .money {
+    color: #ff3535;
+    font-weight: 600;
+    font-size: 0.16rem;
+    i {
+      font-style: normal;
+      font-size: 0.14rem;
+    }
+  }
+
+  /deep/.van-cell__title {
+    font-size: 0.16rem;
+    color: #000000;
+    flex: 1 auto;
+    flex-basis: 47%;
+  }
+
+  /deep/.van-cell__value {
+    height: 0.2rem;
+    display: flex;
+    align-items: center;
+    justify-content: flex-end;
+  }
+}
+.goodsDetail {
+  background: #f3f4f8;
+  /deep/.van-cell-group {
+    margin-bottom: 0.15rem;
+  }
+  /deep/.van-cell {
+    padding: 0.1rem 0.16rem;
+    line-height: 1.5;
+    font-size: 0.14rem;
+    color: #808080;
+  }
+  .valueStyle {
+    color: #000000;
+  }
+
+  .title-detail {
+    font-size: 0.16rem;
+    color: #000;
+    padding-bottom: 0.1rem;
+  }
+
+  .value-detail {
+    font-size: 0.14rem;
+    color: #808080;
+    text-align: left;
+  }
+
+  .btn-group {
+    background: #fff;
+    position: fixed;
+    bottom: 0;
+    left: 0;
+    width: 100%;
+    padding: 0.1rem 0;
+    padding-bottom: calc(env(safe-area-inset-bottom) / 2);
+    .van-button {
+      font-size: 0.16rem;
+      height: 0.44rem;
+      line-height: 0.42rem;
+    }
+  }
+}
+</style>

+ 1035 - 0
src/views/shopMall/GoodsSale.vue

@@ -0,0 +1,1035 @@
+<template>
+  <div class="goodsOrder">
+    <div ref="goodsOrder">
+      <m-header :backUrl="backUrl" />
+
+      <van-cell-group>
+        <van-field
+          readonly
+          clickable
+          label="订单类型"
+          :value="orderText"
+          input-align="right"
+          placeholder="选择类型"
+          @click="showPicker = true"
+        />
+        <van-popup v-model="showPicker" round position="bottom">
+          <van-picker
+            show-toolbar
+            :columns="columns"
+            @cancel="showPicker = false"
+            @confirm="onConfirm"
+          />
+        </van-popup>
+        <van-field
+          :readonly="true"
+          label="学生姓名"
+          v-model="studentName"
+          placeholder="请输入学生姓名"
+          input-align="right"
+        />
+        <van-field
+          label="选择商品"
+          :readonly="true"
+          @click="
+            () => {
+              goodsStatus = true;
+              hashState('goods');
+            }
+          "
+          is-link
+          placeholder="选择商品"
+          input-align="right"
+        />
+        <div class="studentContainer">
+          <van-cell
+            v-for="(item, index) in goodsList"
+            :key="index"
+            class="input-cell"
+            :center="true"
+          >
+            <template slot="icon">
+              <van-image class="logo" :src="item.pic" alt="" />
+            </template>
+            <template slot="title">
+              <div class="content">
+                <div class="name">
+                  <span style="vertical-align: middle; line-height: 0.16rem">{{
+                    item.name
+                  }}</span>
+                  <van-tag
+                    plain
+                    color="#C2A076"
+                    style="
+                      margin-left: 0;
+                      padding: 0.01rem 0.03rem;
+                      margin-right: 0.05rem;
+                    "
+                    >品牌:{{ item.brandName }}</van-tag
+                  >
+                </div>
+                <div class="operation" @click="onGoodDel(goodsList, item)">
+                  <i class="icon_del"></i>删除
+                </div>
+              </div>
+              <p style="padding: 0.02rem 0; font-size: 0.14rem; color: #808080">
+                型号:{{ item.productSn }}
+              </p>
+              <div class="price-section">
+                <div>
+                  <span class="money"
+                    ><span style="font-weight: 400; font-size: 0.14rem"
+                      >现价:</span
+                    ><i>¥</i>{{ item.price | moneyFormat }}</span
+                  ><del>原价:¥{{ item.originalPrice | moneyFormat }}</del>
+                </div>
+                <div style="font-size: 0.14rem; color: #808080">
+                  ×{{ item.goodsNum }}
+                </div>
+              </div>
+            </template>
+          </van-cell>
+        </div>
+      </van-cell-group>
+
+      <van-cell @click="addressStatus = true" class="cell-address" is-link>
+        <template #icon>
+          <img src="./images/icon-address.png" />
+        </template>
+        <template #title>
+          <div v-if="addressInfo.id">
+            <span class="userName">{{ addressInfo.name }}</span>
+            <span class="phone">
+              {{
+                addressInfo.phoneNumber &&
+                addressInfo.phoneNumber.replace(
+                  /^(\d{3})\d{4}(\d+)/,
+                  "$1****$2"
+                )
+              }}
+            </span>
+          </div>
+          <div class="emtry" v-else>去填写收货地址</div>
+        </template>
+        <template #label v-if="addressInfo.id">
+          <span class="addressInfo"
+            >{{ addressInfo.province }} {{ addressInfo.city }}
+            {{ addressInfo.region }} {{ addressInfo.detailAddress }}</span
+          >
+        </template>
+      </van-cell>
+
+      <!-- 是否用余额支付 支付金额大于0时才会显示是否用余额支付 -->
+      <van-cell-group class="pay-section">
+        <van-cell
+          :disabled="false"
+          title="总价格"
+          title-class="pay-name"
+          value-class="pay-value"
+          :center="true"
+        >
+          <template #default> ¥{{ payCountMoney | moneyFormat }} </template>
+        </van-cell>
+        <!-- <van-field
+          label="减免金额"
+          @input="setNoMore"
+          v-model="marketAmount"
+          type="number"
+          ref="marketInput"
+          placeholder="请输入减免金额"
+          input-align="right"
+        /> -->
+      </van-cell-group>
+
+      <protocol
+        v-model="agreeStatus"
+        :userId="studentId + ''"
+        style="padding-top: 0.08rem"
+      />
+
+      <div class="button-group">
+        <van-button
+          class="btn-sure"
+          type="primary"
+          @click="onRefundSure(obj)"
+          round
+          size="large"
+          >确认</van-button
+        >
+      </div>
+    </div>
+
+    <van-popup
+      class="popup-qrcode"
+      v-model="qrCodeStatus"
+      closeable
+      close-icon="cross"
+      @close="onClose"
+    >
+      <div id="qrcode">
+        <vue-qr
+          :logoSrc="config.imagePath"
+          :text="config.value"
+          :margin="10"
+          :size="220"
+        ></vue-qr>
+      </div>
+      <a
+        id="tt"
+        ref="download"
+        v-show="false"
+        :href="downloadUrl"
+        :download="downloadfilename"
+      ></a>
+      <!-- <p>点击图片进行下载</p> -->
+      <van-button
+        v-if="!headerStatus"
+        color="#01C1B5"
+        :disabled="downloadStatus"
+        type="primary"
+        @click="createPoster"
+        round
+        >下载二维码</van-button
+      >
+    </van-popup>
+
+    <!-- 商品 -->
+    <van-popup
+      v-model="goodsStatus"
+      :lock-scroll="true"
+      position="bottom"
+      :style="{ height: '100%', borderRadius: '0', overflowY: 'auto' }"
+    >
+      <GoodsList v-model="goodsStatus" @getChoiceGood="getChoiceGood" />
+    </van-popup>
+
+    <!-- 选择地址 -->
+    <van-action-sheet
+      v-model="addressStatus"
+      title="选择地址"
+      :style="{ height: '60%' }"
+    >
+      <AddAddress
+        v-model="addressStatus"
+        :addressInfo="addressInfo"
+        :userId="studentId"
+        @onDetail="
+          (item) => {
+            addressInfo = item;
+          }
+        "
+      />
+    </van-action-sheet>
+
+    <!-- 协议 -->
+    <van-popup
+      id="protocolPopup"
+      v-model="popupStatus"
+      position="bottom"
+      style="border-radius: 0 !important"
+    >
+      <m-protocol
+        :protocolHTML="protocolHTML"
+        @onClose="popupStatus = !popupStatus"
+        @onPopupSure="popupStatus = !popupStatus"
+      />
+    </van-popup>
+
+    <van-popup
+      v-model="refundStatus"
+      position="bottom"
+      v-if="refundStatus"
+      style="border-radius: 0 !important"
+    >
+      <m-refund
+        @onClose="refundStatus = !refundStatus"
+        @onPopupSure="onRefundSure"
+        :ids="[1, 2]"
+        :showCoupon="false"
+        :buyList="buyList"
+        :balance="0"
+      />
+      <!-- :balance="this.orderType == 1 ? balance : 0" -->
+    </van-popup>
+
+    <m-payment
+      :closeStatus="isStatus"
+      :amount="payMoney"
+      :payment="payment"
+      @onChangeStatus="onChangeStatus"
+    />
+  </div>
+</template>
+<script>
+import GoodsList from "./model/goodsList";
+import AddAddress from "./model/addAddress";
+import MHeader from "@/components/MHeader";
+import MPayment from "@/components/MPayment";
+import Protocol from "@/components/Protocol";
+import MRefund from "@/components/MRefund";
+import { mallGenerateOrder, mallCartAddAll } from "./api";
+import { getUserCashAccount } from "../service/api";
+import { browser, validStudentUrl } from "@/common/util";
+import qs from "query-string";
+import VueQr from "vue-qr";
+export default {
+  name: "teacherList",
+  components: {
+    MHeader,
+    VueQr,
+    MPayment,
+    Protocol,
+    MRefund,
+    GoodsList,
+    AddAddress,
+  },
+  data() {
+    let query = this.$route.query;
+    // 保存之前输入的内容
+    return {
+      couponObj: {
+        INSTRUMENT: "MUSICAL",
+        ACCESSORIES: "ACCESSORIES",
+        TEACHING: "TEACHING",
+        STAFF: "OTHER",
+      },
+      addressStatus: false,
+      addressInfo: {}, // 选择的地址对象
+      goodsStatus: false,
+      dataList: [],
+      radio: "1",
+      studentId: query.studentId, // 学生编号
+      organId: query.organId,
+      studentName: query.studentName,
+      goodsList: [],
+      marketAmount: null,
+      tempForm: {}, // 临时存数据
+      payType: false, // 是否使用余额
+      balance: 0, // 余额
+      backUrl: {
+        status: true,
+        path: "/serviceStudent?type=shop",
+      },
+      isClick: false,
+      downloadStatus: true,
+      qrCodeStatus: false,
+      downloadUrl: null,
+      downloadfilename: null,
+      sGoodsOrderId: null,
+      config: {
+        value: null, //显示的值、跳转的地址
+        imagePath: require("../../assets/images/logo.png"), //中间logo的地址
+      },
+      headerStatus: true,
+      isStatus: false,
+      payment: {}, // 支付对象
+      payMoney: 0,
+      payCountAmount: 0,
+      loading: false,
+      refundStatus: false,
+      refundSure: false, // 是否确认退费规则
+      buyList: [],
+      disCountList: [],
+      moneyList: [],
+      payCountMoney: 0,
+      protocolHTML: null,
+      agreeStatus: false,
+      popupStatus: false,
+      couponShow: false,
+      couponList: [],
+      valuePirce: 0,
+      dataLists: [],
+      countMoney: 0,
+      groupPrice: 0,
+      obj: null,
+      showPicker: false,
+      columns: ["教务代买", "创建订单"],
+      orderType: null,
+      orderText: null,
+    };
+  },
+  mounted() {
+    // 插入token
+    if (browser().android || browser().iPhone) {
+      this.headerStatus = false;
+    }
+    this.__init();
+
+    window.addEventListener("hashchange", this.onHash, false);
+  },
+  methods: {
+    onHash() {
+      this.refundStatus = false;
+      this.goodsStatus = false;
+    },
+    hashState(status) {
+      // 打开弹窗
+      const type = status === "goods" ? this.goodsStatus : this.refundStatus;
+      if (type) {
+        this.isDestroy = false;
+        const splitUrl = window.location.hash.slice(1).split("?");
+        const query = qs.parse(splitUrl[1]);
+        let times = 0;
+        for (let key in query) {
+          times++;
+        }
+        const origin = window.location.href;
+        const url = times > 0 ? "&cPop=" + +new Date() : "?cPop=" + +new Date();
+        history.pushState("", "", `${origin}${url}`);
+      } else {
+        const splitUrl = window.location.hash.slice(1).split("?");
+        const query = qs.parse(splitUrl[1]);
+        if (query.cPop) {
+          window.history.go(-1);
+        }
+      }
+    },
+    onConfirm(value, index) {
+      this.orderText = value;
+      if (index == 0) {
+        this.orderType = 1;
+      } else if (index == 1) {
+        this.orderType = 2;
+      } else {
+        this.orderType = null;
+      }
+      this.showPicker = false;
+    },
+    onRefundSure(obj) {
+      if (obj) {
+        this.refundStatus = false;
+        this.refundSure = true;
+        this.obj = obj;
+      }
+      // 第一次 判断是否
+      if (this.orderType == 1) {
+        this.onCheckSubmit();
+      } else {
+        this.onCreateCode();
+      }
+    },
+    getChoiceGood(item) {
+      console.log(item, "getChoiceGoods");
+      let goodsList = this.goodsList;
+      let status = false;
+      goodsList.forEach((good) => {
+        if (good.goodsId == item.id) {
+          status = true;
+          ++good.goodsNum;
+        }
+      });
+      // 判断是否有同样的商品
+      if (!status) {
+        goodsList.push(item);
+      }
+      this.goodsStatus = false;
+      // this.resetCoupon();
+      this.calcPrice();
+
+      this.hashState("goods");
+    },
+    onGoodDel(goodsList, item) {
+      this.$dialog
+        .confirm({
+          message: "确定删除该商品",
+          confirmButtonColor: "#01C1B5",
+        })
+        .then(() => {
+          let index = goodsList.indexOf(item);
+          if (index !== -1) {
+            goodsList.splice(index, 1);
+          }
+          // this.resetCoupon();
+          this.calcPrice();
+        });
+    },
+    async __init() {
+      try {
+        await getUserCashAccount({ id: this.studentId }).then((res) => {
+          this.balance = res.data.balance || 0;
+        });
+      } catch {
+        //
+      }
+    },
+    async onCreateCode() {
+      if (!this.onCheckFiled()) {
+        return;
+      }
+      // 确认退费规则
+      if (!this.refundSure && this.payCountMoney - this.marketAmount > 0) {
+        this.refundStatus = true;
+        this.hashState();
+        return;
+      }
+      let form = {
+        studentId: this.studentId,
+        goodsList: JSON.stringify(this.goodsList),
+        marketAmount: this.marketAmount ? this.marketAmount : 0,
+        couponIdList: this.obj.couponIdList,
+      };
+      let formCheckChange = false;
+      let tempForm = this.tempForm;
+      // 判断是否修改过内容
+      if (
+        form.studentId == tempForm.studentId &&
+        form.goodsList == tempForm.goodsList &&
+        form.marketAmount == tempForm.marketAmount &&
+        JSON.stringify(form.couponList) == JSON.stringify(tempForm.couponList)
+      ) {
+        formCheckChange = true;
+      }
+
+      if (this.sGoodsOrderId && formCheckChange) {
+        this.onPosterCode(this.sGoodsOrderId);
+      } else {
+        form.type = 1;
+        this.tempForm = form;
+        this.afterPayMent((res) => {
+          this.sGoodsOrderId = res.data.pay.orderNo;
+          this.onPosterCode(res.data.pay.orderNo);
+        });
+      }
+      setTimeout(() => {
+        this.isClick = false;
+      }, 500);
+    },
+    onPosterCode(goodsId) {
+      this.$refs.goodsOrder.style.filter = "blur(3px)";
+      this.qrCodeStatus = true;
+      let url =
+        validStudentUrl() +
+        "/#/goodsOrderBuyMall?id=" +
+        goodsId +
+        "&studentId=" +
+        this.studentId;
+      // console.log(url);
+      this.config.value = url;
+      // 可以点击下载按钮了
+      this.downloadStatus = false;
+    },
+    async afterPayMent(callBack) {
+      try {
+        let goodsList = this.goodsList;
+        // console.log(goodsList, this.addressInfo);
+        const params = [];
+        goodsList.forEach((good) => {
+          params.push({
+            price: good.groupPurchasePrice,
+            productSkuId: good.id,
+            quantity: good.goodsNum,
+            productId: good.productId,
+            hidden: 1,
+            memberId: this.studentId,
+          });
+        });
+        // 购买时,需要添加商品到购物车
+        const carts = await mallCartAddAll(params);
+        const cartConfirm = carts.data || [];
+        const ids = cartConfirm.reduce((arr, value) => {
+          arr.push(value.id);
+          return arr;
+        }, []);
+        const body = {
+          cartIds: ids,
+          memberReceiveAddressId: this.addressInfo.id,
+          orderAmount: this.payCountMoney,
+          userId: this.studentId,
+        };
+        const res = await mallGenerateOrder(body);
+        if (res.data.orderType == "success") {
+          this.$toast(res.msg);
+          this.$router.push({
+            path: "/paymentResult",
+            query: {
+              type: "on",
+              isBack: "off",
+              groupType: "GOODS_SELL",
+            },
+          });
+        } else {
+          callBack && callBack(res);
+        }
+      } catch {
+        //
+      }
+    },
+    createPoster() {
+      let tempImg = document.querySelector("#qrcode img");
+      this.downloadUrl = tempImg.src;
+      this.downloadfilename = "qrCode.png";
+      this.$toast.loading({
+        duration: 0, // 持续展示 toast
+        forbidClick: true,
+        message: "下载中...",
+      });
+      if (browser().android) {
+        setTimeout(() => {
+          this.$toast.clear();
+          //a 标签下载
+          this.$refs.download.click();
+        }, 2000);
+      } else if (browser().iPhone) {
+        setTimeout(() => {
+          this.$toast.clear();
+          //a 标签下载
+          window.webkit.messageHandlers.DAYA.postMessage(
+            JSON.stringify({
+              api: "downLoadImg",
+              content: {
+                downloadUrl: tempImg.src,
+              },
+            })
+          );
+        }, 2000);
+      }
+    },
+    onClose() {
+      this.$refs.goodsOrder.style.filter = "blur(0px)";
+    },
+    async onChangeStatus(val) {
+      this.isStatus = val;
+      this.__init();
+      this.payType = false;
+      this.calcPrice();
+    },
+    async onCheckSubmit() {
+      if (!this.onCheckFiled()) {
+        return;
+      }
+      // 确认退费规则
+      if (!this.refundSure && this.payCountMoney > 0) {
+        this.refundStatus = true;
+        return;
+      }
+      this.afterPayMent((res) => {
+        this.result = res.data.pay;
+        this.onSubmit();
+      });
+      this.refundSure = false;
+    },
+    onCheckFiled() {
+      if (!this.orderType) {
+        this.$toast("请选择订单类型");
+        return false;
+      }
+      if (this.goodsList.length <= 0) {
+        this.$toast("请选择商品");
+        return false;
+      }
+      // if (this.marketAmount) {
+      //   let reg = /(^[1-9]([0-9]+)?(\.[0-9]{1,2})?$)|(^(0){1}$)|(^[0-9]\.[0-9]([0-9])?$)/;
+      //   if (!reg.test(this.marketAmount)) {
+      //     this.$toast("请选输入正确的减免金额");
+      //     return false;
+      //   }
+      // }
+      // if (
+      //   (this.payCountMoney - Number(this.marketAmount)).toFixed(2) <
+      //   this.groupPrice
+      // ) {
+      //   this.$toast("减免后支付金额不能低于团购价");
+      //   return false;
+      // }
+      // if (this.payCountMoney - this.marketAmount < 0) {
+      //   this.$toast("减免金额不能大于总金额");
+      //   return false;
+      // }
+
+      if (!this.agreeStatus) {
+        this.$toast("请先阅读并同意《产品及服务协议》");
+        return false;
+      }
+      return true;
+    },
+    onSubmit() {
+      // submit 提交
+      let result = this.result;
+      if (result.type == "YQPAY") {
+        let f = result.payMap;
+        document.querySelector("#onSubmit").action = f.host;
+        document.querySelector("#apiContent").value = f.apiContent;
+        document.querySelector("#merNo").value = f.merNo;
+        document.querySelector("#notifyUrl").value = f.notifyUrl;
+        document.querySelector("#sign").value = f.sign;
+        document.querySelector("#signType").value = f.signType;
+        document.querySelector("#timestamp").value = f.timestamp;
+        document.querySelector("#version").value = f.version;
+        document.querySelector("#onSubmit").submit();
+      } else if (result.type == "UNIONPAY") {
+        localStorage.setItem("payInfo", JSON.stringify(result));
+        this.$router.push({
+          path: "/alipay",
+          query: {
+            balance: result.totalPrice,
+          },
+        });
+      } else if (result.type == "ADAPAY") {
+        this.payment = result;
+        this.payMoney = result.payMap.amount;
+        // 开始支付窗口
+        this.isStatus = true;
+      }
+    },
+    onClickCheckbox() {
+      // 使用余额方法
+      this.payType = !this.payType;
+      this.calcPrice();
+    },
+    setCoupon(obj) {
+      if (obj) {
+        this.couponList = obj.couponList;
+        this.valuePirce = obj.valuePirce;
+        this.dataLists = obj.dataList;
+      }
+
+      this.calcPrice();
+    },
+
+    setNoMore() {
+      this.calcPrice();
+    },
+    calcPrice() {
+      let goodsList = this.goodsList;
+      let tempPrice = 0;
+      this.groupPrice = 0;
+      this.buyList = [];
+      this.moneyList = [];
+      this.disCountList = [];
+      goodsList.forEach((item) => {
+        const price = Number((item.goodsNum * item.price).toFixed(2));
+        this.buyList.push({
+          name:
+            item.goodsNum > 1 ? `${item.name} * ${item.goodsNum}` : item.name,
+          type: "购买",
+          price: price,
+          couponType: this.couponObj[item.type],
+        });
+        tempPrice += price;
+        this.groupPrice += price;
+      });
+      // const couponType = {
+      //   FULL_REDUCTION: "满减",
+      //   DISCOUNT: "折扣",
+      // };
+      // if (this.dataLists && this.dataLists.length > 0) {
+      //   this.dataLists.forEach((item) => {
+      //     if (this.couponList.indexOf(item.couponCodeId) != -1) {
+      //       this.disCountList.push({
+      //         name: item.couponName,
+      //         type: couponType[item.couponType],
+      //         price: -item.faceValue.toFixed(2),
+      //       });
+      //     }
+      //   });
+      // }
+
+      // if (this.marketAmount > 0) {
+      //   this.buyList.push({
+      //     name: "减免金额",
+      //     type: "一次性",
+      //     price: -this.marketAmount,
+      //     couponType: "FULLCOUPON",
+      //   });
+      // }
+
+      this.payCountMoney = tempPrice;
+      if (tempPrice - this.marketAmount <= 0) {
+        tempPrice = 0;
+      } else {
+        tempPrice = Number((tempPrice - this.marketAmount).toFixed(2));
+      }
+      this.countMoney = tempPrice;
+      tempPrice -= this.valuePirce;
+      this.moneyList.push({ name: "应付金额", price: tempPrice });
+      // 是否使用余额
+      if (this.payType) {
+        if (tempPrice - this.balance >= 0) {
+          this.moneyList.push({ name: "余额支付", price: this.balance });
+          tempPrice = Number((tempPrice - this.balance).toFixed(2));
+          this.moneyList.push({ name: "现金支付", price: tempPrice });
+        } else {
+          this.moneyList.push({ name: "现金支付", price: 0 });
+          this.moneyList.push({ name: "余额支付", price: tempPrice });
+          tempPrice = 0;
+        }
+      } else {
+        this.moneyList.push({ name: "余额支付", price: 0 });
+        this.moneyList.push({ name: "现金支付", price: tempPrice });
+      }
+      this.payMoney = tempPrice;
+    },
+  },
+  destroyed() {
+    // 销毁页面时
+    window.removeEventListener("hashchange", this.onHash, false);
+    this.$toast.clear();
+    this.qrCodeStatus = false;
+  },
+};
+</script>
+<style lang="less" scoped>
+@import url("../../assets/commonLess/variable.less");
+.goodsOrder {
+  min-height: 100vh;
+}
+.pay-name {
+  // padding-left: 0.1rem;
+  flex: 1 auto;
+  font-weight: bold;
+}
+
+/deep/.van-cell-group {
+  .van-cell {
+    padding: 14px 16px;
+    /deep/.van-cell__title {
+      font-size: 0.17rem;
+      color: @mFontColor;
+      flex: 1 auto;
+      width: 65%;
+    }
+
+    /deep/.van-cell__value {
+      font-size: 0.17rem;
+      flex: 1 auto;
+      width: 50%;
+    }
+  }
+}
+
+/deep/.van-cell-group,
+.cell-address {
+  margin-top: 0.12rem;
+}
+
+.textarea {
+  flex-direction: column;
+
+  /deep/.van-cell__value {
+    padding-top: 0.1rem;
+    flex: 1 auto;
+    width: 100%;
+    font-size: 0.15rem;
+    color: #666;
+  }
+
+  /deep/.van-field__control {
+    color: #666;
+  }
+}
+
+/deep/.van-popup__close-icon--top-right {
+  font-size: 0.24rem;
+  color: #c0c0c0;
+  top: 0.1rem;
+  right: 0.1rem;
+}
+
+/deep/.van-stepper__input {
+  background-color: #fff;
+}
+
+.pay-section {
+  .van-checkbox {
+    float: right;
+
+    /deep/.van-checkbox__icon .van-icon {
+      border-color: #aeb3c0;
+    }
+
+    /deep/.van-checkbox__icon--checked .van-icon {
+      background-color: #01c1b5;
+      border-color: #01c1b5;
+      color: #fff;
+    }
+  }
+
+  .van-cell__value {
+    width: auto;
+  }
+
+  .logo {
+    margin-right: 0.08rem;
+    width: 0.24rem;
+    height: 0.24rem;
+  }
+}
+
+#qrcode {
+  background: #fff;
+  // padding: .05rem;
+  margin: 10px auto 0;
+}
+
+.popup-qrcode {
+  width: 80%;
+  // width: 220px;
+  padding: 0.2rem 0;
+  border-radius: 0.05rem;
+  text-align: center;
+
+  .van-button--primary {
+    margin-top: 0.1rem;
+  }
+
+  .loading-section {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+}
+
+.button-group {
+  margin: 0.3rem 0.26rem 0.2rem;
+
+  .btn-sure {
+    background: @mColor;
+    border: 1px solid @mColor;
+    font-size: 0.18rem;
+  }
+
+  .btn-qrcode {
+    margin-top: 0.15rem;
+    font-size: 0.18rem;
+    background: transparent;
+  }
+}
+
+.pay-value {
+  color: #01c1b5;
+}
+
+.icon_close {
+  position: absolute;
+  right: 0.16rem;
+  top: 0.16rem;
+  font-size: 0.2rem;
+  color: #929292;
+}
+
+.agreeProtocol {
+  display: flex;
+  align-items: center;
+  color: #333333;
+  margin-top: 0.1rem;
+  padding: 0.05rem 0.16rem;
+  font-size: 14px;
+  line-height: 0.2rem;
+
+  .van-checkbox {
+    padding-right: 0.08rem;
+  }
+
+  /deep/.van-checkbox__icon .van-icon {
+    background: #fff;
+  }
+
+  /deep/.van-checkbox__icon--checked .van-icon {
+    color: #fff;
+    background-color: #f85043;
+    border-color: #f85043;
+  }
+
+  span {
+    color: #01c1b5;
+  }
+}
+
+.studentContainer {
+  /deep/.van-cell-group {
+    margin-top: 0;
+  }
+  /deep/.van-cell__title {
+    font-size: 0.16rem;
+    color: @mFontColor;
+    flex: 1 auto;
+    width: 70%;
+  }
+
+  .logo {
+    width: 0.82rem;
+    height: 0.82rem;
+    margin-right: 0.12rem;
+    border-radius: 0.05rem;
+    overflow: hidden;
+  }
+
+  .input-cell {
+    padding: 0.12rem 0.16rem;
+    align-items: flex-start;
+    .van-radio {
+      justify-content: flex-end;
+    }
+
+    .price-section {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      del {
+        font-size: 0.12rem;
+        color: #666666;
+        padding-left: 0.1rem;
+      }
+    }
+
+    .money {
+      color: #ff3535;
+      font-weight: 600;
+      font-size: 0.16rem;
+      i {
+        font-style: normal;
+        font-size: 0.14rem;
+      }
+    }
+  }
+
+  /deep/.van-cell__value {
+    height: 0.2rem;
+  }
+
+  .van-tag {
+    margin-left: 0.08rem;
+  }
+
+  .content {
+    display: flex;
+    align-items: flex-start;
+    justify-content: space-between;
+    .name {
+      // display: flex;
+      // align-items: center;
+    }
+    .operation {
+      font-size: 0.13rem;
+      color: #999;
+      display: flex;
+      align-items: center;
+      width: 1.2rem;
+      justify-content: flex-end;
+      .icon_del {
+        display: inline-block;
+        width: 0.13rem;
+        height: 0.13rem;
+        background: url("../../assets/images/icon_del.png") no-repeat center;
+        background-size: contain;
+        margin-right: 0.02rem;
+      }
+    }
+  }
+}
+
+.cell-address {
+  align-items: center;
+
+  /deep/.van-cell__title {
+    margin-left: 14px;
+  }
+
+  img {
+    width: 0.2rem;
+    height: 0.2rem;
+  }
+}
+</style>

+ 49 - 0
src/views/shopMall/api.js

@@ -0,0 +1,49 @@
+import request from "@/helpers/request";
+
+export function mallHomeContent(data) {
+  return request({
+    baseURL: "/api-mall-portal",
+    url: "/home/content",
+    method: "get",
+    params: data,
+  });
+}
+
+// 获取商品列表
+export const mallProductSearch = (data) => {
+  return request({
+    baseURL: "/api-mall-portal",
+    url: "/product/search",
+    method: "post",
+    data,
+  });
+};
+
+// 获取地址列表
+export const mallAddressList = (data) => {
+  return request({
+    baseURL: "/api-mall-portal",
+    url: `/member/address/list/${data.userId}`,
+    method: "get",
+  });
+};
+
+// 添加到购物车
+export const mallCartAddAll = (data) => {
+  return request({
+    baseURL: "/api-mall-portal",
+    url: "/cart/addAll",
+    method: "post",
+    data,
+  });
+};
+
+// 订单支付
+export const mallGenerateOrder = (data) => {
+  return request({
+    baseURL: "/api-mall-portal",
+    url: "/order/teacher/generateOrder",
+    method: "post",
+    data,
+  });
+};

BIN
src/views/shopMall/images/icon-address.png


BIN
src/views/shopMall/images/icon-sell-out.png


+ 103 - 0
src/views/shopMall/model/addAddress.vue

@@ -0,0 +1,103 @@
+<template>
+  <div class="addressList">
+    <div v-if="dataShow">
+      <div
+        class="address"
+        :class="addressInfo.id === address.id && 'active'"
+        @click="onDetail(address)"
+        v-for="(address, index) in list"
+        :key="index"
+      >
+        <div class="content">
+          <p>
+            <span class="name">{{ address.name }}</span>
+            <span>
+              {{
+                address.phoneNumber &&
+                  address.phoneNumber.replace(/^(\d{3})\d{4}(\d+)/, "$1****$2")
+              }}
+            </span>
+          </p>
+          <p class="addressContent">
+            {{ address.province }} {{ address.city }} {{ address.region }}
+            {{ address.detailAddress }}
+          </p>
+        </div>
+      </div>
+    </div>
+    <m-empty class="empty" msg="暂无地址" v-else />
+  </div>
+</template>
+
+<script>
+import { mallAddressList } from "../api";
+import MEmpty from "@/components/MEmpty";
+export default {
+  name: "addAddress",
+  props: ["userId", "addressInfo"],
+  components: { MEmpty },
+  data() {
+    return {
+      list: [],
+      dataShow: true,
+    };
+  },
+  async mounted() {
+    try {
+      const { data } = await mallAddressList({
+        userId: this.userId,
+      });
+      this.list = data;
+      this.dataShow = this.list.length > 0 ? true : false;
+    } catch {
+      //
+    }
+  },
+  methods: {
+    onDetail(item) {
+      this.$emit("onDetail", item);
+      this.$emit("input", false);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.address {
+  padding: 14px;
+  font-size: 16px;
+  color: #666666;
+  line-height: 22px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  p {
+    padding: 4px 0;
+    display: flex;
+    align-items: baseline;
+  }
+  .name {
+    font-size: 18px;
+    font-weight: 600;
+    color: #1a1a1a;
+    line-height: 25px;
+    padding-right: 15px;
+    max-width: 150px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    display: inline-block;
+  }
+
+  .addressContent {
+    display: block;
+    width: 3.2rem;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  }
+}
+.active {
+  background: #f7f7f7;
+}
+</style>

+ 353 - 0
src/views/shopMall/model/addGoodsCart.vue

@@ -0,0 +1,353 @@
+<template>
+  <div class="addGoodsCart">
+    <van-cell title-style="padding-left: 12px">
+      <template #icon>
+        <div class="goodsSection">
+          <van-image :src="selectItem.pic" class="goodsImg" fit="cover" />
+          <div class="sellOut" v-if="selectItem.stock <= 0">
+            <img
+              src="../images/icon-sell-out.png"
+              fit="cover"
+              class="sellOutImg"
+            />
+          </div>
+        </div>
+      </template>
+      <template #title>
+        <div class="goodsInfo">
+          <p class="goodsPrice">
+            <span>¥</span>
+            {{ selectItem.price | moneyFormat }}
+          </p>
+          <p class="goodsStore">库存:{{ selectItem.stock }}</p>
+        </div>
+      </template>
+    </van-cell>
+    <van-cell>
+      <template #title>
+        <div class="title">规格</div>
+      </template>
+      <template #label>
+        <van-radio-group class="radio-group" :value="radio">
+          <van-radio
+            v-for="(item, index) in skuStockList"
+            :key="index"
+            class="radio"
+            :name="item.id"
+            @click="
+              () => {
+                if (radio == item.id) return;
+                radio = item.id;
+              }
+            "
+          >
+            <van-tag
+              size="large"
+              :plain="item.id === radio ? true : false"
+              :type="item.id === radio ? 'primary' : 'default'"
+            >
+              {{ item.spDataJson }}
+            </van-tag>
+          </van-radio>
+        </van-radio-group>
+      </template>
+    </van-cell>
+    <van-cell
+      title="购买数量"
+      style="margin: 12px 0"
+      :border="false"
+      title-class="title"
+      center
+    >
+      <van-stepper
+        v-model="total"
+        input-width="50px"
+        theme="round"
+        buttonSize="24px"
+        :max="selectItem.stock > 200 ? 200 : selectItem.stock"
+        :min="1"
+        :disabled="selectItem.stock <= 0"
+        integer
+      />
+    </van-cell>
+    <div class="btnGroup" style="margin-bottom: 8px">
+      <van-button
+        block
+        round
+        type="info"
+        text="确定"
+        :disabled="selectItem.stock <= 0"
+        @click="() => onAddCart()"
+      />
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: "addGoodsCart",
+  props: {
+    show: {
+      type: Boolean,
+      default: false,
+    },
+    item: {
+      type: Object,
+      default: {},
+    },
+    defaultRadio: {
+      type: Number,
+      default: 0,
+    },
+    showType: {
+      type: String,
+      default: "cart",
+    },
+    onGetCartCount: {
+      type: Function,
+      default: (n) => {},
+    },
+  },
+  watch: {
+    show(val) {
+      // 添加购物车显示
+      if (val) {
+        this.totalData = {};
+        this.total = 1;
+        this.radio = "";
+        this.setList();
+      }
+    },
+  },
+  data() {
+    return {
+      radio: "",
+      total: 1,
+      totalData: {},
+      skuStockList: [],
+    };
+  },
+  computed: {
+    selectItem() {
+      const radio = this.radio;
+      const select = this.skuStockList.find((n) => n.id == radio);
+      if (select) {
+        let stock = select.stock - select.lockStock; //- select.cartNum
+        return {
+          ...select,
+          stock,
+        };
+      }
+      return {
+        stock: 0,
+      };
+    },
+  },
+  mounted() {
+    this.setList();
+  },
+  methods: {
+    setList() {
+      // 处理规格
+      let skuStockList = [];
+      const item = JSON.parse(JSON.stringify(this.item));
+      if (Array.isArray(item.skuStockList)) {
+        skuStockList = item.skuStockList.map((n) => {
+          n.pic = n.pic || item.pic;
+          n.cartNum = 0;
+          if (n.spData) {
+            const spData = JSON.parse(n.spData);
+            let str = "";
+            spData.forEach((sp) => {
+              str += `${sp.value}`;
+            });
+            n.spDataJson = str;
+          } else {
+            n.spDataJson = "默认";
+          }
+          n.lockStock = n.lockStock > 0 ? n.lockStock : 0;
+          return {
+            ...n,
+          };
+        });
+      }
+      if (!skuStockList.length) return skuStockList;
+      // 处理默认显示
+      let index = 0;
+      if (this.defaultRadio) {
+        let i = skuStockList.findIndex((n) => n.id == this.defaultRadio);
+        index = i > -1 ? i : 0;
+      }
+      this.radio = skuStockList[index].id;
+      this.skuStockList = skuStockList;
+    },
+    async onAddCart() {
+      const selectItem = this.selectItem;
+      const item = this.item;
+      // const body = {
+      //   price: selectItem.price, //添加到购物车的价格
+      //   productSkuId: selectItem.id,
+      //   quantity: this.total, // 数量
+      //   productId: item.id,
+      //   hidden: this.showType === "cart" ? 0 : 1,
+      //   promoterId: this.$route.query.promoterId
+      //     ? this.$route.query.promoterId
+      //     : undefined,
+      // };
+      // goodsNum: 1, // 默认数据为1件
+      //     goodsId: item.id,
+      //     image: item.image,
+      //     goodsName: item.name,
+      //     marketPrice: item.marketPrice,
+      //     discountPrice: item.discountPrice,
+      //     complementGoodsIdList: item.complementGoodsIdList,
+      //     groupPurchasePrice: item.groupPurchasePrice,
+      //     type: item.type,
+
+      // marketPrice: item.marketPrice,
+      //     discountPrice: item.discountPrice,
+      //     specification: item.specification,
+      //     brand: item.brand,
+      //     image: item.image,
+      //     name: item.name,
+      //     id: item.id,
+      //     type: item.type,
+      // console.log(selectItem, item);
+      this.$emit("onAddCart", {
+        goodsNum: this.total,
+        productSn: item.productSn,
+        name: item.name,
+        brandName: item.brandName,
+        originalPrice: item.originalPrice,
+        ...this.selectItem,
+      });
+    },
+
+    // 更新产品规格的库存
+    setProductStock(n) {
+      // 根据当前用户的购物车,当前产品规格的数量,限制库存
+      for (let i = 0; i < this.skuStockList.length; i++) {
+        if (this.skuStockList[i].id === this.radio) {
+          this.skuStockList[i].cartNum = n;
+        }
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.addGoodsCart {
+  padding-top: 12px;
+
+  /deep/.van-stepper__input {
+    background: #f7f8f9 !important;
+    border-radius: 6px;
+    margin: 0 8px;
+  }
+  /deep/.van-stepper__minus--disabled {
+    opacity: 0.6 !important;
+    color: #fff !important;
+    background: #f7f8f9 !important;
+    border-color: #f7f8f9 !important;
+  }
+  /deep/.van-stepper--round .van-stepper__plus,
+  /deep/.van-stepper--round .van-stepper__minus {
+    background: #01c1b5 !important;
+    color: #fff;
+    border: 1px solid #fff;
+  }
+}
+
+.goodsSection {
+  position: relative;
+  width: 100px;
+  height: 100px;
+  border-radius: 8px;
+  overflow: hidden;
+  .sellOut {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    background: rgba(0, 0, 0, 0.2);
+    padding: 20px;
+    .sellOutImg {
+      width: 0.6rem;
+      height: 0.6rem;
+    }
+  }
+}
+.goodsImg {
+  width: 100px;
+  height: 100px;
+  background: linear-gradient(180deg, #f0f0f0 0%, #d7d7d7 100%);
+  overflow: hidden;
+}
+
+.goodsPrice {
+  padding-top: 8px;
+  font-size: 18px;
+  color: #ff4e19;
+  line-height: 22px;
+  span {
+    font-size: 16px;
+  }
+}
+.goodsStore {
+  font-size: 14px;
+  color: #999999;
+  line-height: 20px;
+}
+
+.title {
+  font-size: 16px;
+  color: #333333;
+  line-height: 22px;
+}
+
+.radio-group {
+  display: flex;
+  flex-wrap: wrap;
+  margin-top: 14px;
+}
+
+.radio {
+  margin-right: 10px;
+  margin-bottom: 8px;
+
+  /deep/.van-radio__icon {
+    display: none;
+  }
+  /deep/.van-tag--large {
+    height: 32px;
+    font-size: 16px;
+    text-align: center;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  /deep/.van-tag {
+    box-sizing: border-box;
+  }
+  /deep/.van-tag--default {
+    color: #999999;
+    background-color: #f8f8f8;
+  }
+  /deep/.van-tag--primary {
+    background-color: #f7f8f9;
+  }
+  /deep/.van-radio__label {
+    margin-left: 0;
+  }
+}
+
+.btnGroup {
+  padding: 0 15px 15px;
+  margin-bottom: 8px;
+}
+</style>

+ 333 - 0
src/views/shopMall/model/goodsList.vue

@@ -0,0 +1,333 @@
+<template>
+  <div class="goodsList">
+    <van-sticky>
+      <m-header :backUrl="backUrl2" :isFixed="false" name="商品列表" />
+      <!-- <template #left>
+        <van-dropdown-menu
+          active-color="#01C1B5"
+          style=" padding-right: 0.1rem;"
+        >
+          <van-dropdown-item :title="valueText1" v-model="value1" :options="option1" @change="onOptionChange" />
+          <van-dropdown-item
+            :title="valueText2"
+            v-model="value2"
+            :options="option2"
+            @change="onOptionChange"
+          />
+        </van-dropdown-menu>
+      </template> -->
+      <search @onSearch="onSearch" placeholder="请输入商品名称"> </search>
+      <van-dropdown-menu active-color="#01C1B5" style="background-color: #fff">
+        <van-dropdown-item
+          :title="valueText1"
+          v-model="value1"
+          :options="option1"
+          @change="onOptionChange"
+        />
+        <van-dropdown-item
+          :title="valueText2"
+          v-model="value2"
+          :options="option2"
+          @change="onOptionChange"
+        />
+      </van-dropdown-menu>
+    </van-sticky>
+    <div>
+      <van-list
+        v-model="loading"
+        class="studentContainer"
+        v-if="dataShow"
+        key="data"
+        :immediate-check="false"
+        :finished="finished"
+        finished-text=""
+        @load="getList"
+      >
+        <van-cell-group>
+          <van-cell
+            style="align-items: flex-start;"
+            v-for="(item, index) in dataList"
+            :key="index"
+            class="input-cell"
+            @click="onChoiceGood(item)"
+            :center="true"
+          >
+            <template slot="icon">
+              <div class="logo-section">
+                <van-image class="logo" :src="item.pic" alt="" />
+                <div class="sellOut" v-if="item.stock <= 0">
+                  <img
+                    src="../images/icon-sell-out.png"
+                    fit="cover"
+                    class="sellOutImg"
+                  />
+                </div>
+              </div>
+            </template>
+            <template slot="title">
+              <div
+                style="display: flex;align-items: center;justify-content: space-between;"
+              >
+                {{ item.name }}
+              </div>
+              <van-tag plain color="#C2A076" style="margin: .04rem 0;"
+                >品牌:{{ item.brandName }}</van-tag
+              >
+              <p style="padding: .02rem 0;font-size: .14rem; color: #808080;">
+                型号:{{ item.productSn }}
+              </p>
+              <div class="price-section">
+                <div>
+                  <span class="money"
+                    ><span style="font-weight: 400; font-size: .14rem;"
+                      >现价:</span
+                    ><i>¥</i>{{ item.price | moneyFormat }}</span
+                  ><del>原价:¥{{ item.originalPrice | moneyFormat }}</del>
+                </div>
+                <div style="font-size: .14rem; color: #808080">×1</div>
+              </div>
+            </template>
+          </van-cell>
+        </van-cell-group>
+      </van-list>
+      <m-empty class="empty" msg="暂无商品" v-else />
+    </div>
+    <van-popup
+      v-model="addGoodsShow"
+      closeable
+      position="bottom"
+      round
+      @close="
+        () => {
+          addGoodsShow = false;
+        }
+      "
+    >
+      <addGoodsCart
+        :show="addGoodsShow"
+        :item="selectGoodsItem"
+        @onAddCart="onAddCart"
+      />
+    </van-popup>
+  </div>
+</template>
+
+<script>
+import MHeader from "@/components/MHeader";
+import MEmpty from "@/components/MEmpty";
+import Search from "@/components/Search";
+import addGoodsCart from "./addGoodsCart";
+import { mallHomeContent, mallProductSearch } from "../api";
+export default {
+  components: { MHeader, MEmpty, Search, addGoodsCart },
+  data() {
+    return {
+      loading: false,
+      finished: false,
+      params: {
+        pageNum: 1,
+        pageSize: 20,
+      },
+      dataShow: true, // 是否有数据
+      backUrl2: {
+        callBack: () => {
+          this.$emit("input", false);
+        },
+      },
+      value1: "all",
+      valueText1: "类型: 全部",
+      option1: [],
+      value2: "all",
+      valueText2: "分类: 全部",
+      search: null,
+      option2: [],
+      dataList: [],
+      addGoodsShow: false,
+      selectGoodsItem: {},
+    };
+  },
+  async mounted() {
+    try {
+      // 获取分类数据
+      const res = await mallHomeContent({});
+      const all = {
+        text: "全部",
+        value: "all",
+      };
+      this.option1 = [all];
+      const productCategoryList = res.data.productCategoryList || [];
+      productCategoryList.forEach((item) => {
+        this.option1.push({
+          text: item.name,
+          value: item.id,
+        });
+      });
+      this.option2 = [all];
+      const productAttributeCategoryList =
+        res.data.productAttributeCategoryList || [];
+      productAttributeCategoryList.forEach((item) => {
+        this.option2.push({
+          text: item.name,
+          value: item.id,
+        });
+      });
+    } catch {}
+
+    this.loading = true;
+    this.getList();
+  },
+  methods: {
+    onSearch(value) {
+      // 商品搜索
+      this.search = value;
+      this.params.pageNum = 1;
+      this.dataList = [];
+      this.loading = true;
+      this.finished = false;
+      this.dataShow = true;
+      this.getList();
+    },
+    onOptionChange() {
+      this.option1.forEach((item) => {
+        if (item.value == this.value1) {
+          this.valueText1 = item.value == "all" ? "类型: 全部" : item.text;
+        }
+      });
+
+      this.option2.forEach((item) => {
+        if (item.value == this.value2) {
+          this.valueText2 = item.value == "all" ? "分类: 全部" : item.text;
+        }
+      });
+
+      this.params.pageNum = 1;
+      this.dataList = [];
+      this.dataShow = true;
+      this.loading = true;
+      this.finished = false;
+      this.getList();
+    },
+    onAddCart(item) {
+      this.addGoodsShow = false;
+      this.$emit("getChoiceGood", { ...item });
+      this.$emit("input", false);
+    },
+    onChoiceGood(item) {
+      this.selectGoodsItem = item;
+      this.addGoodsShow = true;
+    },
+    async getList() {
+      try {
+        let params = this.params;
+        params.productAttributeCategoryId =
+          this.value1 == "all" ? null : this.value1;
+        params.productCategoryId = this.value2 == "all" ? null : this.value2;
+        params.keyword = this.search ? this.search : null;
+        const res = await mallProductSearch(params);
+        this.loading = false;
+        let result = res.data;
+        params.pageNum = result.pageNum;
+        this.dataList = this.dataList.concat(result.list);
+        if (params.pageNum >= result.totalPage) {
+          this.finished = true;
+        }
+        params.pageNum++;
+        // 判断是否有数据
+        this.dataShow = this.dataList.length <= 0 ? false : true;
+      } catch {
+        this.finished = true;
+        this.dataShow = false;
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+@import url("../../../assets/commonLess/variable.less");
+.studentContainer {
+  /deep/.van-cell-group {
+    margin-top: 0;
+  }
+  /deep/.van-cell__title {
+    font-size: 0.16rem;
+    color: @mFontColor;
+    // flex: 1 auto;
+    flex-basis: 70% !important;
+  }
+  .logo-section {
+    flex-basis: 30%;
+    position: relative;
+  }
+
+  .logo {
+    width: 0.82rem;
+    height: 0.82rem;
+    // margin-right: .12rem;
+    border-radius: 0.05rem;
+    overflow: hidden;
+  }
+
+  .input-cell {
+    padding: 0.12rem 0.16rem;
+
+    .van-radio {
+      justify-content: flex-end;
+    }
+  }
+
+  /deep/.van-cell__value {
+    height: 0.2rem;
+  }
+
+  .van-tag {
+    margin-left: 0.08rem;
+  }
+}
+.input-cell {
+  .price-section {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    del {
+      font-size: 0.12rem;
+      color: #666666;
+      padding-left: 0.1rem;
+    }
+  }
+
+  .money {
+    color: #ff3535;
+    font-weight: 600;
+    font-size: 0.16rem;
+    i {
+      font-style: normal;
+      font-size: 0.14rem;
+    }
+  }
+}
+/deep/.van-dropdown-menu__bar {
+  background: transparent;
+  box-shadow: inherit;
+  height: 34px;
+}
+
+.sellOut {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  width: 0.82rem;
+  height: 0.82rem;
+  background: rgba(0, 0, 0, 0.2);
+  border-radius: 0.05rem;
+  .sellOutImg {
+    width: 0.6rem;
+    height: 0.6rem;
+  }
+}
+</style>

+ 198 - 0
src/views/shopMall/model/studentInstrument.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="goods">
+    <m-header :backUrl="backUrl2" name="学员乐器列表" />
+    <div>
+      <van-list
+        v-model="loading"
+        class="studentContainer"
+        v-if="dataShow"
+        key="data"
+        :immediate-check="false"
+        :finished="finished"
+        finished-text=""
+        @load="getStudentList"
+      >
+        <van-cell-group>
+          <van-cell
+            class="input-cell"
+            :center="true"
+            v-for="item in dataList"
+            :key="item.id"
+            @click="selectGood(item)"
+          >
+            <template slot="icon">
+              <van-image :src="item.goodsImg" class="logo">
+                <template v-slot:loading>
+                  <van-loading type="spinner" size="20" />
+                </template>
+              </van-image>
+              <!-- <img class="logo" src="https://daya.ks3-cn-beijing.ksyun.com/202102/SO1d5Za.jpg" alt=""> -->
+              <!-- <img class="logo" v-else src="@/assets/images/icon_student.png" alt=""> -->
+            </template>
+            <template slot="title">
+              <div
+                style="
+                  display: flex;
+                  align-items: center;
+                  justify-content: space-between;
+                "
+              >
+                {{ item.goodsName }} <span>{{ item.goodsBrand }}</span>
+              </div>
+              <p style="padding: 0.02rem 0; font-size: 0.14rem; color: #808080">
+                型号:{{ item.specification }}
+              </p>
+              <van-tag
+                plain
+                v-if="item.open"
+                color="#01C1B5"
+                style="margin: 0; background: #f2fffc"
+                >乐器保养</van-tag
+              >
+              <van-tag plain v-else color="#C0C0C0" style="margin: 0"
+                >乐器保养</van-tag
+              >
+            </template>
+          </van-cell>
+        </van-cell-group>
+      </van-list>
+      <m-empty class="empty" msg="暂无乐器列表" v-else key="data" />
+    </div>
+  </div>
+</template>
+
+<script>
+import MHeader from "@/components/MHeader";
+import { studentInstrumentGetList } from "./api";
+import MEmpty from "@/components/MEmpty";
+import dayjs from "dayjs";
+import isBetween from "dayjs/plugin/isBetween";
+dayjs.extend(isBetween);
+export default {
+  name: "goods",
+  components: { MEmpty, MHeader },
+  props: {
+    studentId: [String, Number],
+    organId: [String, Number],
+  },
+  data() {
+    return {
+      backUrl2: {
+        callBack: () => {
+          this.$emit("input", false);
+        },
+      },
+      dataShow: true,
+      loading: false,
+      finished: false,
+      dataList: [],
+      params: {
+        search: null,
+        page: 1,
+        rows: 20,
+      },
+    };
+  },
+  mounted() {
+    this.loading = true;
+    this.getStudentList();
+  },
+  methods: {
+    async getStudentList() {
+      let params = this.params;
+      params.studentId = this.studentId;
+      params.organId = this.organId;
+      await studentInstrumentGetList(params).then((res) => {
+        let result = res.data;
+        this.loading = false;
+        if (result.code == 200) {
+          let rows = result.data || [];
+          params.page = rows.pageNo;
+          rows.rows.forEach((item) => {
+            if (dayjs().isBetween(item.startTime, item.endTime)) {
+              item.open = 1;
+            } else {
+              item.open = 0;
+            }
+          });
+          this.dataList = this.dataList.concat(rows.rows);
+          if (params.page >= rows.totalPage) {
+            this.finished = true;
+          }
+          this.params.page++;
+        } else {
+          this.finished = true;
+        }
+        // 判断是否有数据
+        if (this.dataList.length <= 0) {
+          this.dataShow = false;
+        }
+      });
+    },
+    selectGood(item) {
+      this.$emit("getChoiceInstrument", item);
+      this.$emit("input", false);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+@import url("../../../assets/commonLess/variable.less");
+.studentContainer {
+  /deep/.van-cell-group {
+    margin-top: 0;
+  }
+  /deep/.van-cell__title {
+    font-size: 0.16rem;
+    color: @mFontColor;
+    flex: 1 auto;
+  }
+
+  .logo {
+    width: 0.82rem;
+    height: 0.82rem;
+    margin-right: 0.12rem;
+    border-radius: 0.05rem;
+    overflow: hidden;
+  }
+
+  .input-cell {
+    padding: 0.12rem 0.16rem;
+
+    .van-radio {
+      justify-content: flex-end;
+    }
+  }
+
+  /deep/.van-cell__value {
+    height: 0.2rem;
+  }
+
+  .van-tag {
+    margin-left: 0.08rem;
+  }
+}
+.input-cell {
+  .price-section {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    del {
+      font-size: 0.12rem;
+      color: #666666;
+      padding-left: 0.1rem;
+    }
+  }
+
+  .money {
+    color: #ff3535;
+    font-weight: 600;
+    font-size: 0.16rem;
+    i {
+      font-style: normal;
+      font-size: 0.14rem;
+    }
+  }
+}
+</style>

+ 45 - 40
vue.config.js

@@ -1,12 +1,13 @@
-let targetUrl = 'https://mteatest.dayaedu.com'
+// let targetUrl = 'https://mteatest.dayaedu.com'
+let targetUrl = "http://192.168.3.26:8000";
 // let targetUrl = 'http://192.168.3.20:8000'
 // let targetUrl = 'https://online.dayaedu.com'
 // let targetUrl = 'http://dev.dayaedu.com/'
 // let targetUrl = 'http://192.168.3.124:8000'
 module.exports = {
-  chainWebpack: config => {
-    config.devtool('inline-source-map')
-    config.output.filename('[name].[hash].js').end();
+  chainWebpack: (config) => {
+    config.devtool("inline-source-map");
+    config.output.filename("[name].[hash].js").end();
     // // chunkHash
     // config.output.filename(`js/[name].[chunkhash].${Version}.js`).end();
     // config.output.chunkFilename(`js/[id].[chunkhash].${Version}.js`).end();
@@ -19,12 +20,12 @@ module.exports = {
     //   args[0].chunksSortMode = 'none'
     //   return args
     // })
-    config.plugin('html').tap(args => {
+    config.plugin("html").tap((args) => {
       args[0].minify = {
-        removeAttributeQuotes: false
-      }
-      return args
-    })
+        removeAttributeQuotes: false,
+      };
+      return args;
+    });
   },
   // eslint-loader 是否在保存的时候检查
   lintOnSave: true,
@@ -33,65 +34,69 @@ module.exports = {
   runtimeCompiler: false,
   // 生产环境 sourceMap
   productionSourceMap: false,
-  configureWebpack: () => { },
+  configureWebpack: () => {},
   // 配置 webpack-dev-server 行为。
   devServer: {
-    open: process.platform === 'darwin',
-    host: '0.0.0.0',
+    open: process.platform === "darwin",
+    host: "0.0.0.0",
     port: 9999,
     // https: true,
     hotOnly: false,
     // 查阅 https://github.com/vuejs/vue-doc-zh-cn/vue-cli/cli-service.md#配置代理
     proxy: {
-      '/contracts': {
+      "/contracts": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/contracts': '/contracts',
-        xfwd: true
+        "^/contracts": "/contracts",
+        xfwd: true,
       },
-      '/api-student': {
+      "/api-student": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/api-student': '/api-student',
-        xfwd: true
+        "^/api-student": "/api-student",
+        xfwd: true,
       },
-      '/api-cms': {
+      "/api-cms": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/api-cms': '/api-cms',
-        xfwd: true
+        "^/api-cms": "/api-cms",
+        xfwd: true,
       },
-      '/api-teacher': {
+      "/api-teacher": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/api-teacher': '/api-teacher',
-        xfwd: true
+        "^/api-teacher": "/api-teacher",
+        xfwd: true,
       },
-      '/api-web': {
+      "/api-web": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/api-web': '/api-web',
-        xfwd: true
+        "^/api-web": "/api-web",
+        xfwd: true,
       },
-      '/api-auth': {
+      "/api-auth": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/api-auth': '/api-auth',
-        xfwd: true
+        "^/api-auth": "/api-auth",
+        xfwd: true,
       },
-      '/api-oa': {
+      "/api-mall-portal": {
+        target: targetUrl,
+        changeOrigin: true,
+      },
+      "/api-oa": {
         target: targetUrl,
         changeOrigin: true,
         ws: true,
-        '^/api-oa': '/api-oa',
-        xfwd: true
-      }
+        "^/api-oa": "/api-oa",
+        xfwd: true,
+      },
     }, // string | Object
   },
   css: {
@@ -99,11 +104,11 @@ module.exports = {
       less: {
         modifyVars: {
           // red: '#03a9f4',
-          blue: '#01C1B5',
+          blue: "#01C1B5",
           // orange: '#f08d49',
-          // 'text-color': '#111' 
-        }
-      }
-    }
+          // 'text-color': '#111'
+        },
+      },
+    },
   },
-}
+};