فهرست منبع

Merge branch 'master' of http://git.cc-lotus.info/pointToNetwork/web-admin

guhongwei 2 سال پیش
والد
کامیت
5d89afe348

+ 10 - 4
src/layout/data/menu.js

@@ -21,21 +21,27 @@ export const adminMenu = [
       },
       {
         icon: 'icon-rencai',
-        path: '/platmanag/order',
-        name: '平台订单管理',
+        path: '/platmanag/goods',
+        name: '商品管理',
         index: '2-2',
       },
+      {
+        icon: 'icon-rencai',
+        path: '/platmanag/order',
+        name: '订单管理',
+        index: '2-3',
+      },
       {
         icon: 'icon-rencai',
         path: '/platmanag/goodsRate',
         name: '商品评价',
-        index: '2-3',
+        index: '2-4',
       },
       {
         icon: 'icon-rencai',
         path: '/platmanag/storeAcc',
         name: '店铺管理',
-        index: '2-4',
+        index: '2-5',
       },
     ],
   },

+ 12 - 0
src/router/module/platmanag.js

@@ -5,6 +5,18 @@ export default [
     meta: { title: '平台管理-商品标签设置' },
     component: () => import('@/views/platmanag/goodsTags/index.vue'),
   },
+  {
+    path: '/platmanag/goods',
+    name: 'platmanag_goods',
+    meta: { title: '平台管理-商品管理' },
+    component: () => import('@/views/platmanag/goods/index.vue'),
+  },
+  {
+    path: '/platmanag/spec/:id',
+    name: 'platmanag_spec',
+    meta: { title: '平台管理-商品库存管理' },
+    component: () => import('@/views/platmanag/goods/spec.vue'),
+  },
   {
     path: '/platmanag/order',
     name: 'platmanag_order',

+ 6 - 6
src/router/module/selfShop.js

@@ -11,6 +11,12 @@ export default [
     meta: { title: '自营店铺-商品管理' },
     component: () => import('@/views/selfShop/goods/index.vue'),
   },
+  {
+    path: '/selfShop/spec/:id',
+    name: 'selfShop_spec',
+    meta: { title: '自营店铺-商品库存管理' },
+    component: () => import('@/views/selfShop/goods/spec.vue'),
+  },
   {
     path: '/selfShop/order',
     name: 'selfShop_order',
@@ -29,12 +35,6 @@ export default [
     meta: { title: '自营店铺-订单管理-详细信息' },
     component: () => import('@/views/selfShop/order/detail_orderDetail.vue'),
   },
-  {
-    path: '/selfShop/spec/:id',
-    name: 'selfShop_spec',
-    meta: { title: '自营店铺-商品库存管理' },
-    component: () => import('@/views/selfShop/goods/spec.vue'),
-  },
   {
     path: '/selfShop/sales',
     name: 'selfShop_sales',

+ 4 - 0
src/store/module/system/admin.js

@@ -22,6 +22,10 @@ const actions = {
     const res = await this.$axios.$post(`${api.url}`, payload);
     return res;
   },
+  async email({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.url}/emailResetPwd`, payload);
+    return res;
+  },
   async fetch({ commit }, payload) {
     const res = await this.$axios.$get(`${api.url}/${payload}`);
     return res;

+ 22 - 0
src/views/login.vue

@@ -27,6 +27,9 @@
                   <el-form-item class="btn">
                     <el-button type="primary" @click="onSubmit('form')">登录</el-button>
                   </el-form-item>
+                  <el-col :span="24" style="text-align: center">
+                    <el-button type="text" style="color: blue; font-size: 14px" @click="toReset()">忘记密码?</el-button>
+                  </el-col>
                 </el-form>
               </el-col>
             </el-col>
@@ -42,6 +45,8 @@
 const _ = require('lodash');
 import { mapState, createNamespacedHelpers } from 'vuex';
 const { mapActions: maU } = createNamespacedHelpers('admin');
+const { mapActions: admins } = createNamespacedHelpers('admins');
+
 export default {
   name: 'login',
   props: {},
@@ -61,6 +66,7 @@ export default {
   created() {},
   methods: {
     ...maU(['login']),
+    ...admins(['email']),
     onSubmit(formName) {
       this.$refs[formName].validate(async (valid) => {
         if (valid) {
@@ -80,6 +86,22 @@ export default {
         }
       });
     },
+    async toReset() {
+      if (this.form.account == undefined || this.form.account == '') {
+        this.$message.error('请输入账号');
+      } else {
+        this.$confirm('忘记密码,是否确认重置密码?', '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        }).then(async () => {
+          const res = await this.email({ account: this.form.account });
+          if (this.$checkRes(res)) {
+            this.$message({ type: `success`, message: `重置密码成功` });
+          }
+        });
+      }
+    },
   },
   computed: {
     ...mapState(['user']),

+ 1 - 1
src/views/platActivi/act/goodsDetail.vue

@@ -109,7 +109,7 @@ export default {
     test: {
       deep: true,
       immediate: true,
-      handler(val) { },
+      handler(val) {},
     },
   },
 };

+ 8 - 6
src/views/platActivi/coupon/detail.vue

@@ -213,12 +213,14 @@ export default {
           data.tags = _.get(data, 'use_limit_config.tags');
           data.time = _.get(data, 'expire_config.fixed[0]');
           data.days = _.get(data, 'expire_config.days');
-          // if (data.shop) {
-          //   let res = await this.shopFetch(data.shop);
-          //   if (this.$checkRes(res)) {
-          //     data.shop = res.data.name;
-          //   }
-          // }
+          if (data.shop) {
+            let list = [];
+            let res = await this.shopFetch(data.shop);
+            if (this.$checkRes(res)) {
+              list.push(res.data);
+              this.$set(this, 'shopList', list);
+            }
+          }
           this.$set(this, `form`, data);
         }
       } else {

+ 1 - 1
src/views/platActivi/coupon/index.vue

@@ -128,6 +128,7 @@ export default {
     async toEdit({ data }) {
       this.$router.push({ path: '/platActivi/coupon/detail', query: { id: data.id } });
     },
+    // 删除
     async toDel({ data }) {
       let res = await this.delete(data._id);
       if (this.$checkRes(res)) {
@@ -156,7 +157,6 @@ export default {
       if (this.$checkRes(res)) {
         this.$set(this, `statusList`, res.data);
       }
-
       // 减免方式
       res = await this.dictQuery({ code: 'coupon_discount_type' });
       if (this.$checkRes(res)) {

+ 1 - 0
src/views/platfinance/bill/index.vue

@@ -95,6 +95,7 @@ export default {
         this.$message({ type: `warning`, message: `请选择查询信息` });
       }
     },
+    // 重置
     toClose() {
       this.searchForm = {};
       this.search();

+ 2 - 13
src/views/platfinance/statistics/index.vue

@@ -21,7 +21,6 @@
 <script>
 const _ = require('lodash');
 import { mapState, mapGetters, createNamespacedHelpers } from 'vuex';
-const { mapActions } = createNamespacedHelpers('todo');
 export default {
   name: 'index',
   props: {},
@@ -38,18 +37,8 @@ export default {
       data: {},
     };
   },
-  async created() {
-    await this.search();
-  },
-  methods: {
-    ...mapActions(['query', 'fetch', 'create', 'update', 'delete']),
-    async search({ skip = 0, limit = 10, ...info } = {}) {
-      let res = await this.query({ skip, limit, ...info });
-      if (this.$checkRes(res)) {
-        this.$set(this, 'data', res.data);
-      }
-    },
-  },
+  async created() {},
+  methods: {},
   computed: {
     ...mapState(['user']),
   },

+ 342 - 0
src/views/platmanag/goods/index.vue

@@ -0,0 +1,342 @@
+<template>
+  <div id="goods">
+    <template v-if="view === 'list'">
+      <search-1 :form="searchInfo" @onSubmit="search" @querySearch="querySearch" @toReset="toClose" :shopList="shopList"> </search-1>
+      <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"
+        @spec="toSpec"
+        @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 #tags="{ item }">
+              <el-cascader v-model="form[item.model]" :options="tagsList" :props="props" clearable filterable :show-all-levels="false"></el-cascader>
+            </template>
+            <template #act_tags>
+              <el-option v-for="i in act_tagsList" :key="i.value" :label="i.label" :value="i.value"></el-option>
+            </template>
+            <template #status>
+              <el-option v-for="i in goodsStatusList" :key="i.value" :label="i.label" :value="i.value"></el-option>
+            </template>
+            <template #brief>
+              <editor v-model="form.brief" url="/files/point/goods/upload" />
+            </template>
+            <template #shop>
+              <el-select
+                v-model="form.shop"
+                filterable
+                remote
+                reserve-keyword
+                placeholder="请输入商铺名称"
+                :remote-method="searchShop"
+                :loading="loading"
+                @change="selectShop"
+              >
+                <el-option v-for="item in shopList" :key="item.id" :label="item.name" :value="item.id"> </el-option>
+              </el-select>
+            </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: goods } = createNamespacedHelpers('goods');
+const { mapActions: goodsTags } = createNamespacedHelpers('goodsTags');
+const { mapActions: dictData } = createNamespacedHelpers('dictData');
+const { mapActions: actTags } = createNamespacedHelpers('actTags');
+const { mapActions: shop } = createNamespacedHelpers('shop');
+
+export default {
+  name: 'index',
+  props: {},
+  components: { editor: () => import('@/components/editor.vue'), search1: () => import('./parts/search-1.vue') },
+  data: function () {
+    return {
+      view: 'list',
+      fields: [
+        { label: '排序', model: 'sort' },
+        { label: '商品名称', model: 'name' },
+        { label: '店铺名称', model: 'shop.name' },
+        { label: '商品分类', model: 'tags', format: (i) => this.getTags(i) },
+        { label: '活动标签', model: 'act_tags', format: (i) => this.getAct_tags(i) },
+        { 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: 'spec' },
+        { label: '复制', method: 'copy' },
+        { label: '删除', method: 'delete', confirm: true, type: 'danger' },
+      ],
+      btnList: [{ label: '添加', method: 'add' }],
+      searchInfo: {},
+      list: [],
+      total: 0,
+      limit: 50,
+      // info部分
+      infoFields: [
+        { label: '商铺名称', model: 'shop', custom: true },
+        { label: '商品名称', model: 'name' },
+        { label: '商品分类', model: 'tags', custom: true },
+        { label: '活动标签', model: 'act_tags', type: 'selectMany' },
+        { 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: 'file', type: 'upload', url: '/files/point/goods/upload' },
+        { label: '商品介绍', model: 'brief', custom: true },
+      ],
+
+      rules: {
+        shop: [{ required: true, message: '请选择商铺名称', trigger: 'change' }],
+      },
+      form: {},
+      // 商品分类
+      tagsList: [],
+      props: { multiple: true, label: 'label', value: 'code' },
+      // 活动标签
+      act_tagsList: [],
+
+      goodsStatusList: [],
+      // 商铺列表
+      shopList: [],
+      // 店铺远程搜索加载
+      loading: false,
+    };
+  },
+  created() {
+    this.searchOthers();
+    this.search();
+  },
+  methods: {
+    ...dictData({ getDict: 'query' }),
+    ...actTags({ actQuery: 'query' }),
+    ...goodsTags(['tree']),
+    ...shop({ shopQuery: 'query', shopFetch: 'fetch' }),
+    ...goods(['copy', 'query', 'delete', 'fetch', 'update', 'create']),
+    ...methodsUtil,
+    // 查询
+    async search({ skip = 0, limit = this.limit, ...others } = {}) {
+      let query = { skip, limit, ...others };
+      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);
+      }
+    },
+    // 去编辑
+    async toEdit({ data }) {
+      const res = await this.fetch(data._id);
+      if (this.$checkRes(res)) {
+        if (data.shop) {
+          let list = [];
+          let res = await this.shopFetch(data.shop._id);
+          if (this.$checkRes(res)) {
+            list.push(res.data);
+            this.$set(this, 'shopList', list);
+          }
+        }
+        this.$set(this, `form`, res.data);
+        this.view = 'info';
+      } else {
+        this.$message.error('未找到指定数据');
+      }
+    },
+    // 多选
+    handleSelect(data) {
+      this.$emit('handleSelect');
+    },
+    // 店铺名称远程查询
+    async querySearch(value) {
+      this.loading = true;
+      let res = await this.shopQuery({ name: value });
+      if (this.$checkRes(res)) {
+        this.$set(this, 'shopList', res.data);
+      }
+      this.loading = false;
+    },
+    // 店铺名称远程查询
+    selectShop(data) {},
+    async searchShop(value) {
+      let res = await this.shopQuery({ name: value });
+      if (this.$checkRes(res)) {
+        this.$set(this, 'shopList', res.data);
+      }
+    },
+    // 重置
+    toClose() {
+      this.searchInfo = {};
+      this.search();
+    },
+    // 添加自定义
+    initAddData() {
+      const obj = {
+        status: '1',
+      };
+      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 = await this.tree();
+      if (this.$checkRes(res)) this.$set(this, `tagsList`, res.data);
+      res = await this.getDict({ code: 'goods_status' });
+      if (this.$checkRes(res)) this.$set(this, `goodsStatusList`, res.data);
+      res = await this.actQuery();
+      if (this.$checkRes(res)) this.$set(this, `act_tagsList`, res.data);
+    },
+    // 商品状态
+    getStatus(data) {
+      const res = this.goodsStatusList.find((f) => f.value === data);
+      if (res) return res.label;
+      return '';
+    },
+    // 活动标签
+    getAct_tags(data) {
+      const arr = [];
+      for (const val of data) {
+        const r = this.act_tagsList.find((f) => f.value === val);
+        if (r) arr.push(r.label);
+      }
+      return arr.join(';');
+    },
+    // 商品分类
+    getTags(data) {
+      let list = this.tagsList;
+      const getChildren = (list) =>
+        list.map((i) => {
+          if (i.children) return getChildren(i.children);
+          return i;
+        });
+      list = _.flattenDeep(getChildren(list));
+      const arr = [];
+      for (const ts of data) {
+        const last = _.last(ts);
+        const r = list.find((f) => f.code === last);
+        if (r) arr.push(r.label);
+      }
+      return arr.join(';');
+    },
+    // 保存
+    async onSubmit(data) {
+      let res;
+      if (data.id) {
+        data.shop = this.user.shop.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) {
+        data.shop = this.user.shop.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)) {
+          console.log(res.data);
+          this.$message({ type: `success`, message: `下架成功` });
+        }
+        this.search();
+      });
+    },
+    // 库存管理
+    toSpec({ data }) {
+      this.$router.push({ path: `/platmanag/spec/${data._id}` });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 91 - 0
src/views/platmanag/goods/parts/search-1.vue

@@ -0,0 +1,91 @@
+<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="name">
+              <el-input v-model="form.name" placeholder="请输入商品名称" size="small"></el-input>
+            </el-form-item>
+          </el-col>
+          <el-col :span="6">
+            <el-form-item label="店铺名称" prop="type">
+              <el-select
+                v-model="form.shop"
+                filterable
+                remote
+                reserve-keyword
+                placeholder="请输入商铺名称"
+                :remote-method="querySearch"
+                :loading="loading"
+                @change="handleSelect"
+              >
+                <el-option v-for="item in shopList" :key="item.id" :label="item.name" :value="item.id"> </el-option>
+              </el-select>
+            </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 }, shopList: { type: Array } },
+  components: {},
+  data: function () {
+    return {
+      loading: false,
+    };
+  },
+  created() {},
+  methods: {
+    querySearch(value) {
+      this.loading = true;
+      this.$emit('querySearch', value);
+      this.loading = false;
+    },
+    handleSelect(value) {
+      this.$set(this.form, `shop`, value);
+    },
+    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>

+ 236 - 0
src/views/platmanag/goods/spec.vue

@@ -0,0 +1,236 @@
+<template>
+  <div id="spec">
+    <template v-if="view === 'list'">
+      <el-row>
+        <el-col :span="24" class="btn">
+          <el-col :span="2">
+            <el-button size="mini" type="primary" @click="toBack()">返回</el-button>
+          </el-col>
+          <el-col :span="20">
+            <el-breadcrumb separator-class="el-icon-arrow-right">
+              <el-breadcrumb-item v-for="(item, index) in data" :key="index">{{ item.name }}</el-breadcrumb-item>
+            </el-breadcrumb>
+          </el-col>
+        </el-col>
+      </el-row>
+      <data-search :fields="searchFields" v-model="searchInfo" @query="search">
+        <template #status>
+          <el-option v-for="i in statusList" :key="i.model" :label="i.label" :value="i.value"></el-option>
+        </template>
+      </data-search>
+      <data-btn :fields="btnFields" @add="toAdd" />
+      <data-table ref="dataTable" :fields="fields" :opera="opera" :data="list" :total="total" @edit="toEdit" @delete="toDelete" @query="search" @copy="toCopy">
+        <template #code="{ row, item }">
+          <el-link type="primary" @click="toData(row)">{{ row[item.model] }}</el-link>
+        </template>
+      </data-table>
+    </template>
+    <template v-else>
+      <el-row>
+        <el-col :span="24">
+          <el-button type="primary" size="mini" @click="toBackList()">返回</el-button>
+        </el-col>
+        <el-col :span="24">
+          <data-form :span="12" :fields="infoFields" :rules="rules" v-model="form" labelWidth="150px" @save="toSave">
+            <template #status>
+              <el-option v-for="i in statusList" :key="i.model" :label="i.label" :value="i.value"></el-option>
+            </template>
+            <template #can_group>
+              <el-option v-for="i in tfList" :key="i.model" :label="i.label" :value="i.value"></el-option>
+            </template>
+          </data-form>
+        </el-col>
+      </el-row>
+    </template>
+  </div>
+</template>
+
+<script>
+// 找到该商品下的规格定义
+const _ = require('lodash');
+import methodUtil from '@/util/opera';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('goodsSpec');
+const { mapActions: goods } = createNamespacedHelpers('goods');
+const { mapActions: dictData } = createNamespacedHelpers('dictData');
+export default {
+  name: 'spec',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      view: 'list',
+      fields: [
+        { label: '规格名称', model: 'name' },
+        { label: '库存', model: 'num' },
+        { label: '实际销售价格', model: 'sell_money' },
+        { label: '划掉销售价格', model: 'flow_money' },
+        { label: '运费', model: 'freight' },
+        { label: '状态', model: 'status', format: (i) => (i === '0' ? '使用中' : '已禁用') },
+        {
+          label: '是否可以团购',
+          model: 'can_group',
+          format: (i) => {
+            let data = this.tfList.find((f) => f.value == i);
+            if (data) return data.label;
+            else return '暂无';
+          },
+        },
+        { label: '团购金额', model: 'group_config.money' },
+        { label: '开团人数', model: 'group_config.need_person' },
+      ],
+      opera: [
+        { label: '修改', method: 'edit' },
+        { label: '复制', method: 'copy' },
+        { label: '删除', method: 'delete', confirm: true, type: 'danger' },
+      ],
+      list: [],
+      total: 0,
+      limit: 10,
+      btnFields: [{ label: '添加', method: 'add' }],
+      defaultSearch: {},
+      searchInfo: {},
+      searchFields: [
+        { label: '规格名称', model: 'name' },
+        { label: '状态', model: 'status', type: 'select' },
+      ],
+      infoFields: [
+        { label: '规格名称', model: 'name' },
+        { label: '库存', model: 'num', type: 'number' },
+        { label: '实际销售价格', model: 'sell_money', type: 'number' },
+        { label: '划掉销售价格', model: 'flow_money', type: 'number' },
+        { label: '运费', model: 'freight', type: 'number' },
+        { label: '状态', model: 'status', type: 'select' },
+        { label: '是否可以团购', model: 'can_group', type: 'select' },
+        { label: '团购金额', model: 'money', type: 'number' },
+        { label: '开团人数', model: 'need_person', type: 'number' },
+        { label: '图片', model: 'file', type: 'upload', url: '/files/point/goodsSpec/upload' },
+      ],
+      rules: {
+        freight: [{ required: true, message: '请输入运费', trigger: 'blur' }],
+      },
+      form: {},
+
+      statusList: [],
+      // 是否可以团购
+      tfList: [],
+
+      data: [],
+    };
+  },
+  computed: {
+    ...mapState(['user']),
+    goods() {
+      return this.$route.params.id;
+    },
+  },
+  created() {
+    this.defaultSearch.goods = this.goods;
+    this.searchOthers();
+    this.search();
+    this.toData();
+  },
+  methods: {
+    ...dictData({ getDict: 'query' }),
+    ...mapActions(['query', 'fetch', 'update', 'delete', 'create']),
+    ...goods({ goodsFetch: 'fetch' }),
+    ...methodUtil,
+    // 添加自定义
+    initAddData() {
+      const obj = { goods: this.goods, status: '0', can_group: '1', freight: 0 };
+      this.$set(this, 'form', obj);
+    },
+    // 面包屑
+    async toData(row) {
+      let data = this.data;
+      let res = await this.goodsFetch(this.goods);
+      if (this.$checkRes(res)) {
+        data.push(res.data);
+      }
+      this.search();
+    },
+    // 复制
+    async toCopy({ data }) {
+      this.$confirm('是否确认复制该商品?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(async () => {
+        data.name = data.name + '-复制';
+        delete data.id;
+        delete data._id;
+        delete data.meta;
+        delete data.__v;
+        let res;
+        res = await this.create(data);
+        if (this.$checkRes(res)) {
+          this.$message({ type: `success`, message: `复制成功` });
+          this.search();
+          this.toBackList();
+        }
+      });
+    },
+    // 保存
+    async toSave({ data }) {
+      let group_config = {};
+      group_config.money = data.money;
+      group_config.need_person = data.need_person;
+      data.group_config = group_config;
+      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.search();
+        this.toBackList();
+      }
+    },
+    // 修改
+    async toEdit({ data }) {
+      const res = await this.fetch(data._id);
+      if (this.$checkRes(res)) {
+        let data = res.data;
+        const group_config = _.get(data, 'group_config', {});
+        data = { ...data, ...group_config };
+        delete data.group_config;
+        this.$set(this, `form`, data);
+        this.view = 'info';
+      } else {
+        this.$message.error('未找到指定数据');
+      }
+    },
+    // 返回
+    toBack() {
+      this.data.pop();
+      window.history.go('-1');
+    },
+    // 返回列表
+    toBackList() {
+      this.view = 'list';
+      this.form = {};
+    },
+    // 查询其他信息
+    async searchOthers() {
+      // 状态
+      let res = await this.getDict({ code: 'status' });
+      if (this.$checkRes(res)) this.$set(this, 'statusList', res.data);
+      // 是否可以团购
+      res = await this.getDict({ code: 'tf' });
+      if (this.$checkRes(res)) this.$set(this, 'tfList', res.data);
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.btn {
+  margin: 10px 10px 20px 10px;
+  .el-breadcrumb {
+    font-size: 16px;
+    line-height: 30px;
+  }
+}
+</style>

+ 28 - 2
src/views/platmanag/storeAcc/detail.vue

@@ -18,6 +18,7 @@
         @query="search"
         @edit="toEdit"
         @del="toDel"
+        @reset="toReset"
       ></data-table>
     </template>
     <template v-else>
@@ -43,6 +44,7 @@ import methodsUtil from '@/util/opera';
 import { mapState, createNamespacedHelpers } from 'vuex';
 const { mapActions: admins } = createNamespacedHelpers('admins');
 const { mapActions: role } = createNamespacedHelpers('role');
+const { mapActions: emailResetPwd } = createNamespacedHelpers('emailResetPwd');
 export default {
   name: 'index',
   props: {},
@@ -57,6 +59,7 @@ export default {
       ],
       opera: [
         { label: '修改', method: 'edit' },
+        { label: '重置密码', method: 'reset' },
         { label: '删除', method: 'del', confirm: true, type: 'danger' },
       ],
       btnList: [{ label: '添加', method: 'add' }],
@@ -70,7 +73,8 @@ export default {
         { label: '名称', model: 'name' },
         { label: '账号', model: 'account' },
         { label: '角色', model: 'role', type: 'select' },
-        { label: '密码', model: 'password', type: 'password' },
+        { label: '邮箱', model: 'email' },
+        { label: '密码', model: 'password', type: 'password', display: (i) => i.password == '' },
       ],
       rules: {},
       form: {},
@@ -84,7 +88,9 @@ export default {
   methods: {
     ...methodsUtil,
     ...role({ roleQuery: 'query' }),
-    ...admins(['query', 'delete', 'fetch', 'update', 'create']),
+    ...emailResetPwd({ pwdCreate: 'create' }),
+    ...admins(['query', 'delete', 'fetch', 'update', 'create', 'email']),
+    // 重置
     async search({ skip = 0, limit = this.limit, ...others } = {}) {
       others.shop = this.id;
       let query = { skip, limit, ...others };
@@ -95,10 +101,12 @@ export default {
         this.$set(this, `total`, res.total);
       }
     },
+    // 添加自定义
     initAddData() {
       const obj = { shop: this.id };
       this.$set(this, 'form', obj);
     },
+    // 修改
     async toEdit({ data }) {
       const res = await this.fetch(data._id);
       if (this.$checkRes(res)) {
@@ -109,6 +117,20 @@ export default {
         this.$message.error('未找到指定数据');
       }
     },
+    // 重置密码
+    async toReset({ data }) {
+      this.$confirm('忘记密码,是否确认重置密码?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      }).then(async () => {
+        const res = await this.email({ account: data.account });
+        if (this.$checkRes(res)) {
+          this.$message({ type: `success`, message: `重置密码成功` });
+          this.search();
+        }
+      });
+    },
     // 删除
     async toDel({ data }) {
       let res = await this.delete(data._id);
@@ -117,6 +139,7 @@ export default {
         this.search();
       }
     },
+    // 保存
     async toSave({ data }) {
       let res;
       if (data.id) res = await this.update(data);
@@ -126,15 +149,18 @@ export default {
         this.toBackList();
       }
     },
+    // 查询其他信息
     async searchOthers() {
       let res = await this.roleQuery();
       if (this.$checkRes(res)) {
         this.$set(this, `roleList`, res.data);
       }
     },
+    // 返回
     toBack() {
       window.history.go('-1');
     },
+    // 返回列表
     toBackList() {
       this.view = 'list';
       this.form = {};

+ 24 - 8
src/views/selfShop/goods/index.vue

@@ -25,8 +25,9 @@
     </template>
     <template v-else>
       <el-row>
-        <el-col :span="24">
-          <el-button type="primary" size="mini" @click="toBack()">返回</el-button>
+        <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">
@@ -88,7 +89,7 @@ export default {
       searchInfo: {},
       list: [],
       total: 0,
-      limit: 10,
+      limit: 50,
       // info部分
       infoFields: [
         { label: '商品名称', model: 'name' },
@@ -139,7 +140,7 @@ export default {
     initAddData() {
       const obj = {
         status: '1',
-        shop: _.get(this.shop, '_id'),
+        shop: _.get(this.user.shop, '_id'),
       };
       this.$set(this, 'form', obj);
     },
@@ -201,11 +202,27 @@ export default {
       return arr.join(';');
     },
     // 保存
+    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 }) {
-      data.shop = this.user.shop.id;
       let res;
-      if (data.id) res = await this.update(data);
-      else res = await this.create(data);
+      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();
@@ -244,7 +261,6 @@ export default {
         let res;
         if (data._id) res = await this.update(data);
         if (this.$checkRes(res)) {
-          console.log(res.data);
           this.$message({ type: `success`, message: `下架成功` });
         }
         this.search();

+ 33 - 3
src/views/selfShop/goods/spec.vue

@@ -2,8 +2,15 @@
   <div id="spec">
     <template v-if="view === 'list'">
       <el-row>
-        <el-col :span="24" style="padding: 10px">
-          <el-button type="primary" size="mini" @click="toBack()">返回</el-button>
+        <el-col :span="24" class="btn">
+          <el-col :span="2">
+            <el-button size="mini" type="primary" @click="toBack()">返回</el-button>
+          </el-col>
+          <el-col :span="20">
+            <el-breadcrumb separator-class="el-icon-arrow-right">
+              <el-breadcrumb-item v-for="(item, index) in data" :key="index">{{ item.name }}</el-breadcrumb-item>
+            </el-breadcrumb>
+          </el-col>
         </el-col>
       </el-row>
       <data-search :fields="searchFields" v-model="searchInfo" @query="search">
@@ -45,6 +52,8 @@ import methodUtil from '@/util/opera';
 import { mapState, createNamespacedHelpers } from 'vuex';
 const { mapActions } = createNamespacedHelpers('goodsSpec');
 const { mapActions: dictData } = createNamespacedHelpers('dictData');
+const { mapActions: goods } = createNamespacedHelpers('goods');
+
 export default {
   name: 'spec',
   props: {},
@@ -106,6 +115,7 @@ export default {
       statusList: [],
       // 是否可以团购
       tfList: [],
+      data: [],
     };
   },
   computed: {
@@ -118,16 +128,27 @@ export default {
     this.defaultSearch.goods = this.goods;
     this.searchOthers();
     this.search();
+    this.toData();
   },
   methods: {
     ...dictData({ getDict: 'query' }),
     ...mapActions(['query', 'fetch', 'update', 'delete', 'create']),
+    ...goods({ goodsFetch: 'fetch' }),
+
     ...methodUtil,
     // 添加自定义
     initAddData() {
       const obj = { goods: this.goods, status: '0', can_group: '1', freight: 0 };
       this.$set(this, 'form', obj);
     },
+    async toData(row) {
+      let data = this.data;
+      let res = await this.goodsFetch(this.goods);
+      if (this.$checkRes(res)) {
+        data.push(res.data);
+      }
+      this.search();
+    },
     // 复制
     async toCopy({ data }) {
       this.$confirm('是否确认复制该商品?', '提示', {
@@ -180,6 +201,7 @@ export default {
     },
     // 返回
     toBack() {
+      this.data.pop();
       window.history.go('-1');
     },
     // 返回列表
@@ -203,4 +225,12 @@ export default {
 };
 </script>
 
-<style lang="less" scoped></style>
+<style lang="less" scoped>
+.btn {
+  margin: 10px 10px 20px 10px;
+  .el-breadcrumb {
+    font-size: 16px;
+    line-height: 30px;
+  }
+}
+</style>