YY 2 年之前
父节点
当前提交
27eb51ec1b

+ 12 - 0
src/layout/data/menu.js

@@ -167,6 +167,18 @@ export const adminMenu = [
         name: '账单管理',
         index: '6-6',
       },
+      {
+        icon: 'icon-rencai',
+        path: '/selfShop/zrGoods',
+        name: '尊荣商品',
+        index: '6-7',
+      },
+      {
+        icon: 'icon-rencai',
+        path: '/selfShop/zrOrder',
+        name: '尊荣订单',
+        index: '6-8',
+      },
     ],
   },
 ];

+ 13 - 1
src/router/module/selfShop.js

@@ -14,7 +14,7 @@ export default [
   {
     path: '/selfShop/spec/:id',
     name: 'selfShop_spec',
-    meta: { title: '自营店铺-商品库存管理' },
+    meta: { title: '自营店铺-商品管理-库存管理' },
     component: () => import('@/views/selfShop/goods/spec.vue'),
   },
   {
@@ -65,4 +65,16 @@ export default [
     meta: { title: '自营店铺-账单管理' },
     component: () => import('@/views/selfShop/bill/index.vue'),
   },
+  {
+    path: '/selfShop/zrGoods',
+    name: 'selfShop_zrGoods',
+    meta: { title: '自营店铺-尊荣商品' },
+    component: () => import('@/views/selfShop/zrGoods/index.vue'),
+  },
+  {
+    path: '/selfShop/zrOrder',
+    name: 'selfShop_zrOrder',
+    meta: { title: '自营店铺-尊荣订单' },
+    component: () => import('@/views/selfShop/zrOrder/index.vue'),
+  },
 ];

+ 5 - 0
src/store/index.js

@@ -36,6 +36,9 @@ import orderDetail from './module/trade/orderDetail';
 import sot from './module/trade/sot';
 import coupon from './module/trade/coupon';
 
+import zrGoods from './module/zr/zrGoods';
+import zrOrder from './module/zr/zrOrder';
+
 Vue.use(Vuex);
 
 export default new Vuex.Store({
@@ -71,5 +74,7 @@ export default new Vuex.Store({
     getBill,
     outBill,
     admins,
+    zrGoods,
+    zrOrder,
   },
 });

+ 44 - 0
src/store/module/zr/zrGoods.js

@@ -0,0 +1,44 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+const _ = require('lodash');
+Vue.use(Vuex);
+const api = {
+  url: '/point/zr/v1/api/zrGoods',
+};
+
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.url}`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.url}`, payload);
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.url}/${payload}`);
+    return res;
+  },
+  async update({ commit }, payload) {
+    const id = _.get(payload, 'id', _.get(payload, '_id'));
+    const res = await this.$axios.$post(`${api.url}/${id}`, payload);
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.url}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 44 - 0
src/store/module/zr/zrOrder.js

@@ -0,0 +1,44 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+const _ = require('lodash');
+Vue.use(Vuex);
+const api = {
+  url: '/point/zr/v1/api/zrOrder',
+};
+
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.url}`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.url}`, payload);
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.url}/${payload}`);
+    return res;
+  },
+  async update({ commit }, payload) {
+    const id = _.get(payload, 'id', _.get(payload, '_id'));
+    const res = await this.$axios.$post(`${api.url}/${id}`, payload);
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.url}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 226 - 0
src/views/selfShop/zrGoods/index.vue

@@ -0,0 +1,226 @@
+<template>
+  <div id="goods">
+    <template v-if="view === 'list'">
+      <data-search :fields="searchFields" v-model="searchInfo" @query="search">
+        <template #status>
+          <el-option v-for="i in statusList" :key="i.value" :label="i.label" :value="i.value"></el-option>
+        </template>
+      </data-search>
+      <data-btn :fields="btnList" @add="toAdd"></data-btn>
+      <data-table
+        ref="dataTable"
+        :fields="fields"
+        :opera="opera"
+        :data="list"
+        :total="total"
+        :limit="limit"
+        @query="search"
+        @edit="toEdit"
+        @puton="toPuton"
+        @lower="toLower"
+        @delete="toDelete"
+        @copy="toCopy"
+      ></data-table>
+    </template>
+    <template v-else>
+      <el-row>
+        <el-col :span="24" style="margin: 0 0 10px 0">
+          <el-col :span="2"><el-button type="primary" size="mini" @click="toBack()">返回</el-button></el-col>
+          <el-col :span="2"><el-button type="primary" size="mini" @click="onSubmit(form)">保存</el-button></el-col>
+        </el-col>
+        <el-col :span="24">
+          <data-form :fields="infoFields" :rules="rules" v-model="form" labelWidth="150px" @save="toSave">
+            <template #status>
+              <el-option v-for="i in statusList" :key="i.value" :label="i.label" :value="i.value"></el-option>
+            </template>
+            <template #brief>
+              <editor v-model="form.brief" url="/files/point/zrGoods/upload" />
+            </template>
+          </data-form>
+        </el-col>
+      </el-row>
+    </template>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import methodsUtil from '@/util/opera';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: zrGoods } = createNamespacedHelpers('zrGoods');
+const { mapActions: dictData } = createNamespacedHelpers('dictData');
+export default {
+  name: 'index',
+  props: {},
+  components: { editor: () => import('@/components/editor.vue') },
+  data: function () {
+    return {
+      view: 'list',
+      fields: [
+        { label: '排序', model: 'sort' },
+        { label: '商品名称', model: 'name' },
+        { label: '店铺名称', model: 'shop.name' },
+        { label: '商品状态', model: 'status', format: (i) => this.getStatus(i) },
+      ],
+      opera: [
+        { label: '修改', method: 'edit' },
+        { label: '上架', method: 'puton', display: (i) => i.status == '0' },
+        { label: '下架', method: 'lower', display: (i) => i.status == '1' },
+        { label: '复制', method: 'copy' },
+        { label: '删除', method: 'delete', confirm: true, type: 'danger' },
+      ],
+      btnList: [{ label: '添加', method: 'add' }],
+      searchFields: [
+        { label: '商品名称', model: 'name' },
+        { label: '商品状态', model: 'status', type: 'select' },
+      ],
+      searchInfo: {},
+      list: [],
+      total: 0,
+      limit: 50,
+      // info部分
+      infoFields: [
+        { label: '商品名称', model: 'name' },
+        { label: '简短简介', model: 'shot_brief', maxLength: 50 },
+        { label: '预计发货时间', model: 'send_time' },
+        { label: '商品状态', model: 'status', type: 'select' },
+        { label: '商品来源', model: 'source' },
+        { label: '网址', model: 'url' },
+        { label: '排序', model: 'sort', type: 'number' },
+        { label: '消耗尊荣', model: 'cost', type: 'number' },
+        { label: '商品图片', model: 'file', type: 'upload', url: '/files/point/zrGoods/upload' },
+        { label: '商品介绍', model: 'brief', custom: true },
+      ],
+      rules: {},
+      form: {},
+      props: { multiple: true, label: 'label', value: 'code' },
+      statusList: [],
+    };
+  },
+  created() {
+    this.searchOthers();
+    this.search();
+  },
+  methods: {
+    ...dictData({ getDict: 'query' }),
+    ...zrGoods(['query', 'delete', 'fetch', 'update', 'create']),
+    ...methodsUtil,
+    // 查询
+    async search({ skip = 0, limit = this.limit, ...others } = {}) {
+      // let query = { skip, limit, ...others, shop: this.user.shop.id };
+      // if (Object.keys(this.searchInfo).length > 0) query = { ...query, ...this.searchInfo };
+      // const res = await this.query(query);
+      // if (this.$checkRes(res)) {
+      //   this.$set(this, `list`, res.data);
+      //   this.$set(this, `total`, res.total);
+      // }
+    },
+    // 添加自定义
+    initAddData() {
+      const obj = {
+        status: '1',
+        shop: _.get(this.user.shop, '_id'),
+      };
+      this.$set(this, 'form', obj);
+    },
+    // 复制
+    async toCopy({ data }) {
+      this.$confirm('是否确认复制该商品?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(async () => {
+        let res;
+        res = await this.copy(data.id);
+        if (this.$checkRes(res)) {
+          this.$message({ type: `success`, message: `复制成功` });
+          this.toBack();
+          this.search();
+        }
+      });
+    },
+    // 查询其他信息
+    async searchOthers() {
+      let res;
+      res = await this.getDict({ code: 'goods_status' });
+      if (this.$checkRes(res)) this.$set(this, `statusList`, res.data);
+    },
+    // 商品状态
+    getStatus(data) {
+      const res = this.statusList.find((f) => f.value === data);
+      if (res) return res.label;
+      return '';
+    },
+    // 保存
+    async onSubmit(data) {
+      let res;
+      if (data.id) {
+        res = await this.update(data);
+      } else {
+        res = await this.create(data);
+      }
+      if (this.$checkRes(res)) {
+        this.$message({ type: `success`, message: `维护信息成功` });
+        this.toBack();
+        this.search();
+      }
+    },
+    // 保存
+    async toSave({ data }) {
+      let res;
+      if (data.id) {
+        res = await this.update(data);
+      } else {
+        res = await this.create(data);
+      }
+      if (this.$checkRes(res)) {
+        this.$message({ type: `success`, message: `维护信息成功` });
+        this.toBack();
+        this.search();
+      }
+    },
+    // 执行返回
+    toBack() {
+      this.form = {};
+      this.view = 'list';
+    },
+    // 上架
+    async toPuton({ data }) {
+      this.$confirm('是否确认上架该商品?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(async () => {
+        data.status = '1';
+        let res;
+        if (data.id) res = await this.update(data);
+        if (this.$checkRes(res)) {
+          this.$message({ type: `success`, message: `上架成功` });
+        }
+        this.search();
+      });
+    },
+    // 下架
+    async toLower({ data }) {
+      this.$confirm('是否确认下架该商品?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(async () => {
+        data.status = '0';
+        let res;
+        if (data._id) res = await this.update(data);
+        if (this.$checkRes(res)) {
+          this.$message({ type: `success`, message: `下架成功` });
+        }
+        this.search();
+      });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 347 - 0
src/views/selfShop/zrOrder/detail_orderDetail.vue

@@ -0,0 +1,347 @@
+<template>
+  <div id="form-1">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col class="top-btn">
+          <el-button type="primary" size="mini" @click="toBack()">返回</el-button>
+        </el-col>
+        <el-col :span="9" class="one">
+          <el-col :span="24" class="add">
+            <el-col :span="2">
+              <i class="el-icon-location"></i>
+            </el-col>
+            <el-col :span="22">
+              <el-col>
+                <p>{{ address.name }},{{ address.phone }}</p>
+              </el-col>
+              <el-col>
+                <p>{{ address.province }} , {{ address.city }} , {{ address.area }} , {{ address.address }}</p>
+              </el-col>
+            </el-col>
+          </el-col>
+          <el-col :span="24" class="shop"> <i class="el-icon-s-shop"></i>{{ shop.name }}</el-col>
+          <el-col :span="24" v-for="(item, index) in list" :key="index">
+            <el-col :span="24" class="goods bode">
+              <el-col :span="6"><el-image :src="item.goods.file[0].url"></el-image></el-col>
+              <el-col :span="18">
+                <el-col :span="12">
+                  <el-col>
+                    <p>{{ item.goods.name }}</p>
+                  </el-col>
+                  <el-col>
+                    <p>规格:{{ item.name }}</p>
+                  </el-col>
+                </el-col>
+                <el-col :span="12" class="money">
+                  <el-col>
+                    <p v-if="form.type == '0'">¥{{ item.sell_money }}</p>
+                    <p v-else-if="form.type == '1'">¥{{ item.group_config.money }}</p>
+                  </el-col>
+                  <el-col>
+                    <p>X{{ item.buy_num }}</p>
+                  </el-col>
+                  <el-col>
+                    <span class="spanfour" style="color: blue" @click="toUrl(item.goods.url)"> 查看商品来源:{{ item.goods.source }}</span>
+                  </el-col>
+                </el-col>
+              </el-col>
+            </el-col>
+            <el-col :span="24" class="bode">
+              <el-col :span="6">运费</el-col>
+              <el-col :span="18" class="other" v-if="!item.goods.freight == '0'">{{ item.goods.freight }}</el-col>
+              <el-col :span="18" class="other" v-else>包邮</el-col>
+            </el-col>
+          </el-col>
+          <el-col :span="24" class="bode">
+            <el-col :span="6">订单备注</el-col>
+            <el-col :span="18" class="other" v-if="form.remarks">{{ form.remarks }}</el-col>
+            <el-col :span="18" class="other" v-else>暂无备注</el-col>
+          </el-col>
+          <el-col :span="24" class="bode">
+            <el-col :span="6">配送方式</el-col>
+            <el-col :span="18" class="other">快递配送</el-col>
+          </el-col>
+          <el-col :span="24" class="goods_total bode">
+            <el-col :span="6">快递费</el-col>
+            <el-col :span="18" class="other">
+              <p>¥{{ total_detail.freight_total }}</p>
+            </el-col>
+          </el-col>
+          <el-col :span="24" class="goods_total bode">
+            <el-col :span="8">优惠后实付金额</el-col>
+            <el-col :span="16" class="other">
+              <p>¥{{ pay.pay_money }}</p>
+            </el-col>
+          </el-col>
+          <el-col :span="24" class="bode">
+            <el-col :span="6">下单时间</el-col>
+            <el-col :span="18" class="other">{{ form.buy_time }}</el-col>
+          </el-col>
+          <el-col :span="24" class="bode">
+            <el-col :span="6">支付时间</el-col>
+            <el-col :span="18" class="other">{{ form.pay_time }}</el-col>
+          </el-col>
+          <el-col :span="24" class="bode">
+            <el-col :span="6">寄出运单号</el-col>
+            <el-col :span="18" class="other">{{ transport.shop_transport_no || '暂无单号' }}</el-col>
+          </el-col>
+          <el-col :span="24" class="bode">
+            <el-col :span="6">寄出快递类型</el-col>
+            <el-col :span="18" class="other">{{ transport.shop_transport_name || '暂无快递信息' }}</el-col>
+          </el-col>
+          <el-col :span="24" class="form">
+            <el-form :model="form" ref="form" label-width="100px" class="demo-ruleForm">
+              <el-form-item
+                label="寄出运单号"
+                prop="shop_transport_no"
+                v-if="(form.status == '1' || form.status == '2') && (!transport.shop_transport_no || !transport.shop_transport_name)"
+              >
+                <el-input v-model="form.shop_transport_no" placeholder="请输入运单号,快递类型,同时填入" size="small"></el-input>
+              </el-form-item>
+              <el-form-item
+                label="快递类型"
+                prop="shop_transport_type"
+                v-if="(form.status == '1' || form.status == '2') && (!transport.shop_transport_no || !transport.shop_transport_name)"
+              >
+                <el-select
+                  v-model="form.shop_transport_type"
+                  filterable
+                  remote
+                  reserve-keyword
+                  placeholder="请选择快递类型,运单号,同时填入"
+                  :remote-method="querySearch"
+                  :loading="loading"
+                  @change="handleSelect"
+                  size="small"
+                  style="width: 100%"
+                >
+                  <el-option v-for="item in shop_transport_typeList" :key="item.id" :label="item.label" :value="item.value"> </el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="订单状态" prop="status">
+                <el-select v-model="form.status" clearable filterable placeholder="请选择订单状态" size="small" style="width: 100%">
+                  <el-option v-for="i in order_processList" :key="i.label" :label="i.label" :value="i.value"> </el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" @click="onSubmit()">保存</el-button>
+              </el-form-item>
+            </el-form>
+          </el-col>
+        </el-col>
+        <el-col :span="9" class="one" v-if="transport.shop_transport_no">
+          <el-col :span="24">{{ activit.is_check || '暂无快递信息' }}</el-col>
+          <el-col :span="24">{{ transport.shop_transport_name || '暂无快递信息' }}: {{ activit.no || '暂无快递信息' }}</el-col>
+          <el-col :span="24">
+            <p>订单编号: {{ form.id }}</p>
+          </el-col>
+          <el-col :span="24">
+            <p>收货地址:{{ address.province }} , {{ address.city }} , {{ address.area }} , {{ address.address }}</p>
+          </el-col>
+          <el-col :span="24">
+            <el-timeline :reverse="reverse">
+              <el-timeline-item v-for="(activity, index) in activities" :key="index" :timestamp="activity.time" :color="activity.color">
+                {{ activity.context }}
+              </el-timeline-item>
+            </el-timeline>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+const moment = require('moment');
+import { mapState, mapGetters, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('orderDetail');
+const { mapActions: dictData } = createNamespacedHelpers('dictData');
+const { mapActions: sot } = createNamespacedHelpers('sot');
+
+export default {
+  name: 'form-1',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      form: {},
+      // 地址
+      address: {},
+      // 实付金额
+      total_detail: {},
+      // 商铺
+      shop: {},
+      // 运单号
+      transport: {},
+      pay: {},
+      // 商品列表
+      list: [],
+      // 物流
+      reverse: false,
+      activities: [],
+      activit: {},
+      // 订单状态
+      order_processList: [],
+      // 快递类型
+      shop_transport_typeList: [],
+      loading: false,
+    };
+  },
+  async created() {
+    await this.searchOther();
+
+    await this.search();
+  },
+  methods: {
+    ...dictData({ dictQuery: 'query' }),
+    ...sot({ sotFetch: 'fetch' }),
+    ...mapActions(['query', 'fetch', 'create', 'update']),
+    // 查询
+    async search() {
+      let res;
+      res = await this.fetch(this.id);
+      if (this.$checkRes(res)) {
+        this.$set(this, `form`, res.data);
+        // 地址
+        this.$set(this, `address`, res.data.address);
+        this.$set(this, `shop`, res.data.shop);
+        // 商品
+        this.$set(this, `list`, res.data.goods);
+        // 应付金额
+        this.$set(this, `total_detail`, res.data.total_detail);
+        this.$set(this, `pay`, res.data.order.pay);
+        if (res.data.transport) {
+          let arr = await this.dictQuery({ code: 'transport_type', value: res.data.transport.shop_transport_type });
+          if (this.$checkRes(arr)) {
+            let type = arr.data.find((i) => i.value == res.data.transport.shop_transport_type);
+            if (type) res.data.transport.shop_transport_name = type.label;
+            this.$set(this, `transport`, res.data.transport);
+          }
+          res = await this.sotFetch(this.id);
+          if (this.$checkRes(res)) {
+            if (res.errcode == '0') {
+              let activities = res.data.list;
+              activities[0].color = '#0bbd87';
+              this.$set(this, `activities`, activities);
+              this.$set(this, `activit`, res.data);
+            }
+          }
+        }
+      }
+    },
+    async querySearch(value) {
+      this.loading = true;
+      let res = await this.dictQuery({ code: 'transport_type', label: value });
+      if (this.$checkRes(res)) {
+        this.$set(this, 'shop_transport_typeList', res.data);
+      }
+      this.loading = false;
+    },
+    toUrl(url) {
+      if (url) {
+        window.open(url, '_blank');
+      } else {
+        this.$message.error('该商品还未添加网址,无法跳转');
+      }
+    },
+    handleSelect(value) {},
+    // 提交
+    async onSubmit() {
+      let form = this.form;
+      let transport = {};
+      let res;
+      if (form.shop_transport_no && form.shop_transport_type) {
+        transport.shop_transport_no = form.shop_transport_no;
+        transport.shop_transport_type = form.shop_transport_type;
+        form.transport = transport;
+      }
+      if (form.id) res = await this.update(form);
+      if (this.$checkRes(res)) {
+        this.$message({ type: `success`, message: `维护信息成功` });
+        this.toBack();
+      }
+    },
+    // 返回
+    toBack() {
+      window.history.go('-1');
+    },
+    // 查询其他信息
+    async searchOther() {
+      let res;
+      // 减免方式
+      res = await this.dictQuery({ code: 'order_process' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `order_processList`, res.data);
+      }
+    },
+  },
+  computed: {
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  metaform() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    margin: 10px 0 0 5%;
+    padding: 5px;
+    .add {
+      border-bottom: 2px dashed #ccc;
+      margin: 0 0 5px 0;
+      padding: 5px 0;
+    }
+    .shop {
+      padding: 4px 0;
+      font-size: 18px;
+      border-bottom: 1px solid #ccc;
+    }
+    .goods {
+      padding: 10px 0;
+      .money {
+        text-align: right;
+      }
+    }
+    .other {
+      text-align: right;
+      word-break: break-all;
+      p {
+        color: red;
+      }
+      .spanfour {
+        color: blue;
+      }
+    }
+    .el-col {
+      margin: 3px 0;
+    }
+    .bode {
+      border-bottom: 1px solid rgb(238, 237, 237);
+    }
+    .form {
+      margin: 20px 0 0 0;
+    }
+  }
+}
+
+/deep/.el-timeline {
+  margin: 20px 0 0 0;
+  font-size: 16px;
+}
+/deep/.el-timeline-item__node:nth-child(1) {
+  background-color: green;
+}
+</style>

+ 128 - 0
src/views/selfShop/zrOrder/index.vue

@@ -0,0 +1,128 @@
+<template>
+  <div id="card-1">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col :span="24" class="one"> <span>尊荣订单</span> </el-col>
+        <search-1 :form="searchForm" @onSubmit="search" @toReset="toClose"> </search-1>
+        <data-table
+          :select="true"
+          :selected="selected"
+          @handleSelect="handleSelect"
+          :fields="fields"
+          :opera="opera"
+          @query="search"
+          :data="list"
+          :total="total"
+          @detail="toDetail"
+        >
+        </data-table>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('zrOrder');
+const { mapActions: dictData } = createNamespacedHelpers('dictData');
+
+export default {
+  name: 'card-1',
+  props: {},
+  components: { search1: () => import('./parts/search-1.vue') },
+  data: function () {
+    return {
+      searchForm: {},
+      list: [],
+      total: 0,
+      opera: [{ label: '详情', method: 'detail' }],
+      fields: [
+        { label: '订单号', model: 'no' },
+        { label: '下单时间', model: 'buy_time' },
+        { label: '顾客', model: 'customer.name' },
+        { label: '商品', model: 'goods' },
+        { label: '需支付金额', model: 'real_pay' },
+        { label: '商品数量', model: 'buy_num_total' },
+      ],
+      // 多选值
+      selected: [],
+      statusList: [],
+    };
+  },
+  async created() {
+    await this.search();
+    await this.searchOther();
+  },
+  methods: {
+    ...dictData({ dictQuery: 'query' }),
+    ...mapActions(['query', 'fetch', 'create', 'update', 'delete']),
+    // 查询
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      // let condition = _.cloneDeep(this.searchForm);
+      // if (condition.buy_time) {
+      //   condition[`buy_time@start`] = _.head(condition.buy_time);
+      //   condition[`buy_time@end`] = _.last(condition.buy_time);
+      //   delete condition.buy_time;
+      // }
+      // let res = await this.query({ skip, limit, ...condition, ...info, shop: this.user.shop.id });
+      // if (this.$checkRes(res)) {
+      //   this.$set(this, 'list', res.data);
+      //   this.$set(this, 'total', res.total);
+      // }
+    },
+    toDetail({ data }) {
+      this.$router.push({ path: '/selfShop/zrOrder/detail_orderDetail', query: { id: data.id } });
+    },
+    toClose() {
+      this.searchForm = {};
+      this.search();
+    },
+    // 多选
+    handleSelect(data) {
+      this.$emit('handleSelect');
+    },
+    // 查询其他信息
+    async searchOther() {
+      let res;
+      // 类型
+      res = await this.dictQuery({ code: 'order_process' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `statusList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    margin: 0 0 10px 0;
+    span:nth-child(1) {
+      font-size: 20px;
+      font-weight: 700;
+      margin-right: 10px;
+    }
+  }
+  .two {
+    margin: 0 0 10px 0;
+  }
+  .thr {
+    margin: 0 0 10px 0;
+  }
+}
+</style>

+ 134 - 0
src/views/selfShop/zrOrder/parts/parts/detail-1.vue

@@ -0,0 +1,134 @@
+<template>
+  <div id="card-1">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" v-if="num == '1'">
+          <el-col :span="24" class="one">
+            <search-1 :form="searchForm" @onSubmit="search" @querySearch="querySearch" @toReset="toClose" :shopList="shopList"> </search-1>
+          </el-col>
+          <el-col :span="24" class="one">
+            <el-button type="primary" size="mini" @click="toDeliver()">生成发货清单</el-button>
+            <p>(发货清单里不显示售后的订单)</p>
+          </el-col>
+          <data-table
+            :select="true"
+            :selected="selected"
+            @handleSelect="handleSelect"
+            :fields="fields"
+            :opera="opera"
+            @query="search"
+            :data="list"
+            :total="total"
+            @detail="toDetail"
+          >
+          </data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('orderDetail');
+const { mapActions: shop } = createNamespacedHelpers('shop');
+export default {
+  name: 'card-1',
+  props: { statusList: { type: Array } },
+  components: { search1: () => import('../search-1.vue') },
+  data: function () {
+    return {
+      num: '1',
+      searchForm: {},
+      list: [],
+      total: 0,
+      opera: [{ label: '详情', method: 'detail' }],
+      fields: [
+        { label: '订单号', model: 'no' },
+        { label: '下单时间', model: 'buy_time' },
+        { label: '顾客', model: 'customer.name' },
+        { label: '店铺名称', model: 'shop.name' },
+        { label: '支付金额', model: 'real_pay' },
+        { label: '商品数量', model: 'buy_num_total' },
+        { label: '是否售后', model: 'is_afterSale', format: (i) => (i === true ? '该订单有商品申请售后' : '未申请售后') },
+      ],
+      // 多选值
+      selected: [],
+      shopList: [],
+      // 发货清单
+      deliverList: [],
+      deliver: '0',
+    };
+  },
+  async created() {
+    await this.search();
+  },
+  methods: {
+    ...shop({ shopQuery: 'query' }),
+    ...mapActions(['query', 'fetch', 'create', 'update', 'delete']),
+    // 查询
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let condition = _.cloneDeep(this.searchForm);
+      if (condition.buy_time) {
+        condition[`buy_time@start`] = _.head(condition.buy_time);
+        condition[`buy_time@end`] = _.last(condition.buy_time);
+        delete condition.buy_time;
+      }
+      info.status = '1';
+      let res = await this.query({ skip, limit, ...condition, ...info, shop: this.user.shop.id });
+      if (this.$checkRes(res)) {
+        this.$set(this, 'list', res.data);
+        this.$set(this, 'total', res.total);
+      }
+    },
+    // 详情
+    toDetail({ data }) {
+      this.$router.push({ path: '/selfShop/order/detail_orderDetail', query: { id: data._id } });
+    },
+    toClose() {
+      this.searchForm = {};
+      this.search();
+    },
+    // 多选
+    handleSelect(data) {
+      this.$set(this, 'deliverList', data);
+    },
+    // 生成发货清单
+    toDeliver() {
+      this.$emit('toDeliver', { data: this.deliverList });
+    },
+    // 店铺名称远程查询
+    async querySearch(value) {
+      let res = await this.shopQuery({ name: value });
+      if (this.$checkRes(res)) {
+        this.$set(this, 'shopList', res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.one {
+  margin: 0 0 10px 0;
+  p {
+    font-size: 12px;
+    color: #777;
+  }
+}
+</style>

+ 263 - 0
src/views/selfShop/zrOrder/parts/parts/detail-2.vue

@@ -0,0 +1,263 @@
+<template>
+  <div id="form-1">
+    <el-row>
+      <el-col :span="24" class="main animate__animated animate__backInRight">
+        <el-col class="top_btn"><el-button type="primary" size="mini" @click="toBack()">返回</el-button></el-col>
+        <el-col :span="24" class="one"> <span>发货清单</span> </el-col>
+        <el-col class="top_btn"> <el-button type="primary" @click="toFile()">导出清单</el-button></el-col>
+        <el-col :span="24" class="two">
+          <el-col :span="15" v-for="(item, index) in list" :key="index">
+            <el-card class="box-card">
+              <el-col class="clearfix">
+                <el-col>收货人:{{ item.address.name }},{{ item.address.phone }}</el-col>
+                <el-col>收货人地址:{{ item.address.province }},{{ item.address.city }},{{ item.address.area }},{{ item.address.address }}</el-col>
+              </el-col>
+              <el-col class="two_1">
+                <data-table
+                  :select="true"
+                  :selected="selected"
+                  @handleSelect="(e) => handleSelect(e, item.address)"
+                  rowKey="index"
+                  :usePage="false"
+                  :fields="fields"
+                  :data="item.goodsList"
+                >
+                </data-table>
+              </el-col>
+            </el-card>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import FileSaver from 'file-saver';
+const ExcelJS = require('exceljs');
+import { mapState, mapGetters, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'form-1',
+  props: { deliverList: { type: Array } },
+  components: {},
+  data: function () {
+    return {
+      list: [],
+      fields: [
+        { label: '订单编号', model: 'order_no' },
+        { label: '商品名称', model: 'goods.name' },
+        { label: '商品规格', model: 'name' },
+        { label: '商品数量', model: 'buy_num' },
+      ],
+      selected: [],
+      fileList: [],
+    };
+  },
+  async created() {
+    await this.search();
+  },
+  methods: {
+    // 查询
+    async search() {
+      let data = this.deliverList;
+      if (data) {
+        let test = _(data).groupBy('address._id').values().value();
+        let list = [];
+        for (const p1 of test) {
+          let goodsList = [];
+          for (const p2 of p1) {
+            if (p2.is_afterSale == false) {
+              for (let p3 of p2.goods) {
+                // _.pick(p3, ['goods', 'name', 'buy_num']);
+                // let p3_2 = (({ goods, name, buy_num }) => ({ goods, name, buy_num }))(p3);
+                delete p3.flow_money;
+                delete p3.freight;
+                delete p3.id;
+                delete p3.status;
+                delete p3._id;
+                delete p3.meta;
+                delete p3.num;
+                delete p3.sell_money;
+                delete p3.__v;
+                delete p3.tags;
+                delete p3.cart_id;
+                delete p3.can_group;
+                delete p3.group_config;
+                delete p3.url;
+                let good = (({ name }) => ({ name }))(p3.goods);
+                p3.goods = good;
+                p3.order_no = p2.no;
+              }
+              goodsList.push(...p2.goods);
+              let i = 0;
+              for (const p4 of goodsList) {
+                p4.index = i;
+                i++;
+              }
+            }
+          }
+          let address = _.pick(p1[0].address, ['name', 'phone', 'province', 'city', 'area', 'address', '_id']);
+          // let address = (({ name, phone, province, city, area, address, _id }) => ({ name, phone, province, city, area, address, _id }))(p1[0].address);
+          list.push({ goodsList, address });
+        }
+        this.$set(this, 'list', list);
+      }
+    },
+    // 选中要导出的商品
+    handleSelect(goodsList, address) {
+      let fileList = this.fileList;
+      if (fileList.length == 0) {
+        fileList.push({ goodsList, address });
+      } else {
+        if (goodsList.length > 0) {
+          for (const val of fileList) {
+            if (address._id == val.address._id) {
+              val.goodsList = goodsList;
+            } else {
+              fileList.push({ goodsList, address });
+            }
+          }
+        } else {
+          let p1 = fileList.filter((i) => i.address._id != address._id);
+          fileList = p1;
+        }
+      }
+      let list = _.uniqBy(fileList, 'address._id');
+      this.$set(this, 'fileList', list);
+    },
+    // 导出清单
+    toFile() {
+      const workbook = new ExcelJS.Workbook();
+      let list = this.fileList;
+      for (let [index, p1] of list.entries()) {
+        let name = p1.address.name + index;
+        const worksheet = workbook.addWorksheet(name);
+        // 设置标题-start
+        // 获取单元格位置
+        let titleCell = worksheet.getCell('A1');
+        // 合并单元格
+        worksheet.mergeCells('A1:D1');
+        // 单元格内容
+        titleCell.value = '发货清单';
+        worksheet.columns.forEach(function (column, i) {
+          column.font = {
+            size: 14,
+          };
+          column.width = 38;
+          column.alignment = {
+            wrapText: true,
+            vertical: 'middle',
+            horizontal: 'left',
+          };
+          column.border = {
+            top: { style: 'thin' },
+            left: { style: 'thin' },
+            bottom: { style: 'thin' },
+            right: { style: 'thin' },
+          };
+        });
+        // 单元格内容样式
+        titleCell.style = {
+          alignment: {
+            vertical: 'middle',
+            horizontal: 'center',
+          },
+          font: {
+            size: 20,
+            bold: true,
+          },
+          border: {
+            top: { style: 'thin' },
+            left: { style: 'thin' },
+            bottom: { style: 'thin' },
+            right: { style: 'thin' },
+          },
+        };
+        // 设置标题-end
+        let data = [
+          ['收获人', p1.address.name],
+          ['联系电话', p1.address.phone],
+          ['收货地址', p1.address.province + p1.address.city + p1.address.area + p1.address.address],
+          ['订单号', '产品名称', '产品规格', '购买数量'],
+        ];
+        for (const p2 of p1.goodsList) {
+          let p4 = [[p2.order_no, p2.goods.name, p2.name, p2.buy_num]];
+          data.push(...p4);
+        }
+        const row = worksheet.getRow(1);
+        row.height = 40;
+        for (const val of data) {
+          worksheet.addRow(val);
+        }
+      }
+      workbook.xlsx.writeBuffer().then((buffer) => {
+        FileSaver.saveAs(
+          new Blob([buffer], {
+            type: 'application/octet-stream',
+          }),
+          `发货清单.xlsx`
+        );
+      });
+    },
+    // 返回
+    toBack() {
+      this.$emit('toBack');
+    },
+  },
+  computed: {
+    data() {
+      return this.$route.query.data;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .top_btn {
+    margin: 0 0 10px 0;
+  }
+  .one {
+    margin: 0 0 10px 0;
+    span:nth-child(1) {
+      font-size: 20px;
+      font-weight: 700;
+      margin-right: 10px;
+    }
+  }
+  .two {
+    margin: 5px 10%;
+    .data-table {
+      margin: 5px 0;
+    }
+    .clearfix {
+      margin: 4px 0;
+      .el-col {
+        margin: 4px 0;
+      }
+    }
+    .box-card {
+      margin: 5px;
+      padding: 5px 0 20px 0;
+    }
+    .shop {
+      text-align: center;
+      font-size: 18px;
+    }
+    .item {
+      margin-bottom: 18px;
+    }
+  }
+}
+</style>

+ 78 - 0
src/views/selfShop/zrOrder/parts/search-1.vue

@@ -0,0 +1,78 @@
+<template>
+  <div id="search-1">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-form :model="form" ref="form" label-width="130px">
+          <el-col :span="6">
+            <el-form-item label="订单号" prop="no">
+              <el-input v-model="form.no" placeholder="请输入订单号" size="small"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="7">
+            <el-form-item label="下单日期" prop="buy_time">
+              <el-date-picker
+                v-model="form.buy_time"
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                value-format="yyyy-MM-dd"
+              >
+              </el-date-picker>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24" class="btn">
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="onSubmit('form')">搜索</el-button>
+            <el-button icon="el-icon-refresh" size="mini" @click="toReset('form')">重置</el-button>
+          </el-col>
+        </el-form>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'search-1',
+  props: { form: { type: Object } },
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {
+    onSubmit() {
+      this.$emit('onSubmit');
+    },
+    toReset(formName) {
+      this.$refs[formName].resetFields();
+      this.$emit('toReset');
+    },
+  },
+  computed: {},
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .btn {
+    text-align: right;
+  }
+  /deep/.el-form-item {
+    float: left;
+    width: 100%;
+    margin: 0 0 10px 0;
+  }
+}
+</style>