Yahoo hace 4 años
padre
commit
8ede4fabfa

+ 3 - 1
public/config.js

@@ -3,5 +3,7 @@ window.publicConfig = {
   APP_NAME: '萤火商城系统2.0',
   // 必填: api地址, 换成自己的域名即可
   // 例如: https://www.你的域名.com/index.php?s=/store
-  BASE_API: '../index.php?s=/store'
+  // BASE_API: '../index.php?s=/store'
+  BASE_API: 'http://yoshop2-feel.cn/index.php?s=/store'
+  // BASE_API: 'http://yoshop2-feel.cn/index.php?s=/store'
 }

+ 32 - 0
src/api/client/h5/setting.js

@@ -0,0 +1,32 @@
+import { axios } from '@/utils/request'
+
+/**
+ * api接口列表
+ */
+const api = {
+  detail: '/client.h5.setting/detail',
+  update: '/client.h5.setting/update'
+}
+
+/**
+ * 获取设置
+ */
+export function detail (key) {
+  return axios({
+    url: api.detail,
+    method: 'get',
+    params: { key }
+  })
+}
+
+/**
+ * 更新设置
+ * @param {*} data
+ */
+export function update (key, data) {
+  return axios({
+    url: api.update,
+    method: 'post',
+    data: { key, ...data }
+  })
+}

+ 0 - 30
src/api/client/wxapp.js

@@ -1,30 +0,0 @@
-import { axios } from '@/utils/request'
-
-/**
- * api接口列表
- */
-const api = {
-  detail: '/client.wxapp/detail',
-  setting: '/client.wxapp/setting'
-}
-
-// 微信小程序详情
-export function detail (params) {
-  return axios({
-    url: api.detail,
-    method: 'get',
-    params
-  })
-}
-
-/**
- * 更新记录
- * @param {*} data
- */
-export function setting (data) {
-  return axios({
-    url: api.setting,
-    method: 'post',
-    data
-  })
-}

+ 32 - 0
src/api/client/wxapp/setting.js

@@ -0,0 +1,32 @@
+import { axios } from '@/utils/request'
+
+/**
+ * api接口列表
+ */
+const api = {
+  detail: '/client.wxapp.setting/detail',
+  update: '/client.wxapp.setting/update'
+}
+
+/**
+ * 获取设置
+ */
+export function detail (key) {
+  return axios({
+    url: api.detail,
+    method: 'get',
+    params: { key }
+  })
+}
+
+/**
+ * 更新设置
+ * @param {*} data
+ */
+export function update (key, data) {
+  return axios({
+    url: api.update,
+    method: 'post',
+    data: { key, ...data }
+  })
+}

+ 27 - 0
src/api/order/export.js

@@ -0,0 +1,27 @@
+import { axios } from '@/utils/request'
+
+/**
+ * api接口列表
+ */
+const api = {
+  exportOrder: '/order.export/exportOrder',
+  list: '/order.export/list'
+}
+
+// 订单导出
+export function exportOrder (params) {
+  return axios({
+    url: api.exportOrder,
+    method: 'get',
+    params
+  })
+}
+
+// 获取订单导出记录
+export function list (params) {
+  return axios({
+    url: api.list,
+    method: 'get',
+    params
+  })
+}

+ 11 - 0
src/common/enum/order/export/ExportStatus.js

@@ -0,0 +1,11 @@
+import Enum from '../../enum'
+
+/**
+ * 枚举类:订单导出状态
+ * ExportStatusEnum
+ */
+export default new Enum([
+  { key: 'NORMAL', name: '进行中', value: 10 },
+  { key: 'COMPLETED', name: '已完成', value: 20 },
+  { key: 'FAIL', name: '失败', value: 30 }
+])

+ 3 - 0
src/common/enum/order/export/index.js

@@ -0,0 +1,3 @@
+import ExportStatusEnum from './ExportStatus'
+
+export { ExportStatusEnum }

+ 33 - 3
src/config/router.config.js

@@ -217,7 +217,19 @@ export const asyncRouterMap = [
             component: () => import(/* webpackChunkName: "order" */ '@/views/order/refund/Detail'),
             meta: { title: '售后单详情', keepAlive: false, permission: ['/order/refund/detail'] },
             hidden: true
-          }
+          },
+          {
+            path: '/order/tools',
+            component: RouteView,
+            meta: { title: '订单处理', keepAlive: false, permission: ['/order/tools'] },
+            children: [
+              {
+                path: '/order/tools/export',
+                component: () => import(/* webpackChunkName: "order" */ '@/views/order/tools/Export'),
+                meta: { title: '订单导出', keepAlive: false, permission: ['/order/tools/export'] }
+              },
+            ]
+          },
         ]
       },
 
@@ -408,6 +420,11 @@ export const asyncRouterMap = [
         component: RouteView,
         meta: { title: '客户端', keepAlive: true, icon: Icons.mpweixin, iconStyle: { fontSize: '17.2px', color: '#36b313' }, permission: ['/client'] },
         children: [
+          {
+            path: '/client/register',
+            component: () => import(/* webpackChunkName: "client" */ '@/views/client/Register'),
+            meta: { title: '注册设置', keepAlive: false, permission: ['/client/register'] }
+          },
           {
             path: '/client/wxapp',
             component: RouteView,
@@ -416,11 +433,24 @@ export const asyncRouterMap = [
             children: [
               {
                 path: '/client/wxapp/setting',
-                component: () => import(/* webpackChunkName: "mp" */ '@/views/client/wxapp/Setting'),
+                component: () => import(/* webpackChunkName: "client" */ '@/views/client/wxapp/Setting'),
                 meta: { title: '小程序设置', keepAlive: false, permission: ['/client/wxapp/setting'] }
               }
             ]
-          }
+          },
+          {
+            path: '/client/h5',
+            component: RouteView,
+            redirect: '/client/h5/setting',
+            meta: { title: 'H5端', keepAlive: false, permission: ['/client/h5'] },
+            children: [
+              {
+                path: '/client/h5/setting',
+                component: () => import(/* webpackChunkName: "client" */ '@/views/client/h5/Setting'),
+                meta: { title: '站点设置', keepAlive: false, permission: ['/client/h5/setting'] }
+              }
+            ]
+          },
         ]
       },
 

+ 2 - 2
src/global.less

@@ -156,13 +156,13 @@ ol {
       margin: 0 3px;
     }
 
-    p.extra,
+    .extra,
     small {
       color: rgba(0, 0, 0, 0.45);
       font-size: 13px !important;
     }
 
-    p.extra {
+    .extra {
       margin-bottom: 5px;
     }
   }

BIN
src/mock.zip


+ 14 - 2
src/utils/utils.less

@@ -207,6 +207,10 @@
   margin-bottom: 15px;
 }
 
+.mb-20 {
+  margin-bottom: 20px !important;
+}
+
 .ml-5 {
   margin-left: 5px;
 }
@@ -227,12 +231,16 @@
   margin-right: 20px;
 }
 
-
 .mlr-2 {
   margin-left: 2px;
   margin-right: 2px;
 }
 
+.mlr-5 {
+  margin-left: 5px;
+  margin-right: 5px;
+}
+
 .mtb-2 {
   margin-top: 2px;
   margin-bottom: 2px;
@@ -244,7 +252,11 @@
 }
 
 .c-muted-1 {
-  color: #7b7b7b;
+  color: #7b7b7b !important;
+}
+
+.c-red {
+  color: #f00 !important;
 }
 
 // 字体大小

+ 180 - 0
src/views/client/Register.vue

@@ -0,0 +1,180 @@
+<template>
+  <a-card :bordered="false">
+    <div class="card-title">{{ $route.meta.title }}</div>
+    <a-spin :spinning="isLoading">
+      <a-form :form="form" @submit="handleSubmit">
+        <a-form-item label="默认登录/注册方式" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-radio-group v-decorator="['registerMethod', { rules: [{ required: true }] }]">
+            <a-radio :value="10">手机号 + 短信验证码</a-radio>
+          </a-radio-group>
+          <div class="form-item-help">
+            <p class="extra">
+              <small>发送短信服务需要先配置</small>
+              <router-link target="_blank" :to="{ path: '/setting/sms' }">短信通知设置</router-link>
+            </p>
+            <p class="extra">使用手机号作为主账户可以实现多种客户端的账户统一,例如H5和微信小程序</p>
+          </div>
+        </a-form-item>
+
+        <a-divider orientation="left">微信小程序授权登录</a-divider>
+
+        <a-form-item label="一键授权登录/注册" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-radio-group v-decorator="['isOauthMpweixin', { rules: [{ required: true }] }]">
+            <a-radio :value="1">
+              <span>开启</span>
+              <a-tag class="ml-5" color="green">推荐</a-tag>
+            </a-radio>
+            <a-radio :value="0">关闭</a-radio>
+          </a-radio-group>
+          <div class="form-item-help">
+            <small>开启后在微信小程序端一键获取用户授权并登录和注册</small>
+          </div>
+        </a-form-item>
+
+        <a-form-item
+          v-show="form.getFieldValue('isOauthMpweixin') == 1"
+          label="注册时绑定手机号"
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+        >
+          <a-radio-group v-decorator="['isForceBindMpweixin', { rules: [{ required: true }] }]">
+            <a-radio :value="1">
+              <span>强制绑定</span>
+              <a-tag class="ml-5" color="green">推荐</a-tag>
+            </a-radio>
+            <a-radio :value="0">不绑定</a-radio>
+          </a-radio-group>
+          <div class="form-item-help">
+            <p class="extra">开启后在微信小程序端一键授权注册时强制绑定手机号,仅首次注册时弹出</p>
+            <p
+              v-show="form.getFieldValue('isForceBindMpweixin') == 0"
+              class="extra c-red"
+            >如果不强制绑定手机号,会造成多端情况下同一个用户注册多个账户,强烈推荐绑定手机号</p>
+          </div>
+        </a-form-item>
+
+        <a-form-item
+          v-show="form.getFieldValue('isOauthMpweixin') == 1 && form.getFieldValue('isForceBindMpweixin') == 0"
+          label="手动绑定手机号"
+          :labelCol="labelCol"
+          :wrapperCol="wrapperCol"
+        >
+          <a-radio-group v-decorator="['isManualBind', { rules: [{ required: true }] }]">
+            <a-radio :value="1">显示</a-radio>
+            <a-radio :value="0">不显示</a-radio>
+          </a-radio-group>
+          <div class="form-item-help">
+            <small>用户在个人中心页可以手动操作绑定手机号</small>
+          </div>
+        </a-form-item>
+
+        <a-form-item :wrapper-col="{ span: wrapperCol.span, offset: labelCol.span }">
+          <a-button type="primary" html-type="submit">提交</a-button>
+        </a-form-item>
+      </a-form>
+    </a-spin>
+  </a-card>
+</template>
+
+<script>
+import pick from 'lodash.pick'
+import { isEmpty } from '@/utils/util'
+import * as Api from '@/api/setting/store'
+import SettingSmsSceneEnum from '@/common/enum/setting/sms/Scene'
+
+export default {
+  data () {
+    return {
+      SettingSmsSceneEnum,
+      // 当前设置项的key
+      key: 'register',
+      // 标签布局属性
+      labelCol: { span: 4 },
+      // 输入框布局属性
+      wrapperCol: { span: 10 },
+      // loading状态
+      isLoading: false,
+      // 当前表单元素
+      form: this.$form.createForm(this),
+      // 当前记录详情
+      record: {}
+    }
+  },
+  // 初始化数据
+  created () {
+    // 获取当前详情记录
+    this.getDetail()
+  },
+  methods: {
+
+    // 获取当前详情记录
+    getDetail () {
+      this.isLoading = true
+      Api.detail(this.key)
+        .then(result => {
+          // 当前记录
+          this.record = result.data.values
+          // 设置默认值
+          this.setFieldsValue()
+        })
+        .finally(result => {
+          this.isLoading = false
+        })
+    },
+
+    /**
+     * 设置默认值
+     */
+    setFieldsValue () {
+      const { record, $nextTick, form } = this
+      !isEmpty(form.getFieldsValue()) && $nextTick(() => {
+        form.setFieldsValue(pick(record, ['registerMethod', 'isManualBind', 'isOauthMpweixin', 'isForceBindMpweixin']))
+      })
+    },
+
+    /**
+     * 确认按钮
+     */
+    handleSubmit (e) {
+      e.preventDefault()
+      // 表单验证
+      const { form: { validateFields } } = this
+      validateFields((errors, values) => {
+        // 提交到后端api
+        !errors && this.onFormSubmit(values)
+      })
+    },
+
+    /**
+    * 提交到后端api
+    */
+    onFormSubmit (values) {
+      this.isLoading = true
+      Api.update(this.key, { form: values })
+        .then((result) => {
+          // 显示提示信息
+          this.$message.success(result.message, 1.5)
+        })
+        .finally((result) => {
+          this.isLoading = false
+        })
+    }
+
+  }
+}
+</script>
+<style lang="less" scoped>
+.ant-form-item {
+  margin-bottom: 15px;
+}
+/deep/.ant-form-item-control {
+  padding-left: 10px;
+
+  .ant-form-item-control {
+    padding-left: 0;
+  }
+}
+.ant-divider {
+  margin-top: 50px !important;
+}
+</style>

+ 140 - 0
src/views/client/h5/Setting.vue

@@ -0,0 +1,140 @@
+<template>
+  <a-card :bordered="false">
+    <div class="card-title">{{ $route.meta.title }}</div>
+    <a-spin :spinning="isLoading">
+      <a-form :form="form" @submit="handleSubmit">
+        <a-form-item class="mt-30" label="是否开启访问" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-radio-group v-decorator="['enabled', { rules: [{ required: true }] }]">
+            <a-radio :value="1">开启</a-radio>
+            <a-radio :value="0">关闭</a-radio>
+          </a-radio-group>
+          <div class="form-item-help">
+            <p class="extra">
+              <span>注:如关闭,用户则无法通过H5端访问商城</span>
+            </p>
+          </div>
+        </a-form-item>
+        <a-form-item label="H5站点地址" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-input
+            v-decorator="['baseUrl', { rules: [{ required: true, message: '请输入H5站点地址' }] }]"
+          />
+          <div class="form-item-help">
+            <p class="extra">
+              <span>请填写H5端实际的访问地址,以</span>
+              <a-tag class="mlr-5">https://</a-tag>开头; 斜杠
+              <a-tag class="mlr-5">/</a-tag>
+              <span>结尾</span>
+            </p>
+            <p class="extra">
+              <span>例如:https://www.aaa.com/</span>
+            </p>
+          </div>
+        </a-form-item>
+        <a-form-item :wrapper-col="{ span: wrapperCol.span, offset: labelCol.span }">
+          <a-button type="primary" html-type="submit">提交</a-button>
+        </a-form-item>
+      </a-form>
+    </a-spin>
+  </a-card>
+</template>
+
+<script>
+import pick from 'lodash.pick'
+import { isEmpty } from '@/utils/util'
+import * as Api from '@/api/client/h5/setting'
+
+export default {
+  data () {
+    return {
+      // 标签布局属性
+      labelCol: { span: 4 },
+      // 输入框布局属性
+      wrapperCol: { span: 10 },
+      // loading状态
+      isLoading: false,
+      // 当前表单元素
+      form: this.$form.createForm(this),
+      // 当前设置项key
+      key: 'basic',
+      // 当前记录详情
+      record: {}
+    }
+  },
+  // 初始化数据
+  created () {
+    // 获取当前详情记录
+    this.getDetail()
+  },
+  methods: {
+
+    // 获取当前详情记录
+    getDetail () {
+      this.isLoading = true
+      Api.detail(this.key)
+        .then(result => {
+          // 当前记录
+          this.record = result.data.detail
+          // 设置默认值
+          this.setFieldsValue()
+        })
+        .finally(result => {
+          this.isLoading = false
+        })
+    },
+
+    /**
+     * 设置默认值
+     */
+    setFieldsValue () {
+      const { record, $nextTick, form } = this
+      !isEmpty(form.getFieldsValue()) && $nextTick(() => {
+        form.setFieldsValue(pick(record, ['enabled', 'baseUrl']))
+      })
+    },
+
+    /**
+     * 确认按钮
+     */
+    handleSubmit (e) {
+      e.preventDefault()
+      // 表单验证
+      const { form: { validateFields } } = this
+      validateFields((errors, values) => {
+        // 提交到后端api
+        !errors && this.onFormSubmit(values)
+      })
+    },
+
+    /**
+    * 提交到后端api
+    */
+    onFormSubmit (values) {
+      this.isLoading = true
+      Api.update(this.key, { form: values })
+        .then((result) => {
+          // 显示提示信息
+          this.$message.success(result.message, 1.5)
+        })
+        .finally((result) => {
+          this.isLoading = false
+        })
+    }
+
+  }
+}
+</script>
+<style lang="less" scoped>
+.ant-form-item {
+  margin-bottom: 24px;
+}
+/deep/.ant-form-item-control {
+  padding-left: 10px;
+
+  .ant-form-item-control {
+    padding-left: 0;
+  }
+}
+.ant-divider {
+  margin-top: 60px !important;
+}
+</style>

+ 5 - 3
src/views/client/wxapp/Setting.vue

@@ -50,7 +50,7 @@
 <script>
 import pick from 'lodash.pick'
 import { isEmpty } from '@/utils/util'
-import * as Api from '@/api/client/wxapp'
+import * as Api from '@/api/client/wxapp/setting'
 
 export default {
   data () {
@@ -63,6 +63,8 @@ export default {
       isLoading: false,
       // 当前表单元素
       form: this.$form.createForm(this),
+      // 当前设置项key
+      key: 'basic',
       // 当前记录详情
       record: {}
     }
@@ -77,7 +79,7 @@ export default {
     // 获取当前详情记录
     getDetail () {
       this.isLoading = true
-      Api.detail()
+      Api.detail(this.key)
         .then(result => {
           // 当前记录
           this.record = result.data.detail
@@ -117,7 +119,7 @@ export default {
     */
     onFormSubmit (values) {
       this.isLoading = true
-      Api.setting({ form: values })
+      Api.update(this.key, { form: values })
         .then((result) => {
           // 显示提示信息
           this.$message.success(result.message, 1.5)

+ 6 - 3
src/views/market/points/Setting.vue

@@ -33,6 +33,9 @@
         <div v-show="form.getFieldValue('is_shopping_gift') == 1">
           <a-form-item label="积分赠送比例" :labelCol="labelCol" :wrapperCol="wrapperCol">
             <a-input-number
+              :min="0.1"
+              :max="100"
+              :precision="1"
               v-decorator="['gift_ratio', { rules: [{ required: true, message: '请输入积分赠送比例' }] }]"
             />
             <span class="ml-10">%</span>
@@ -61,7 +64,7 @@
             <InputNumberGroup
               addonBefore="1个积分可抵扣"
               addonAfter="元"
-              :inputProps="{ min: 0.01 }"
+              :inputProps="{ min: 0.01, precision: 2 }"
               v-decorator="['discount.discount_ratio', { rules: [{ required: true, message: '积分抵扣比例不能为空' }] }]"
             />
             <div class="form-item-help">
@@ -73,7 +76,7 @@
               <InputNumberGroup
                 addonBefore="订单满"
                 addonAfter="元"
-                :inputProps="{ min: 0.01 }"
+                :inputProps="{ min: 0.01, precision: 2 }"
                 v-decorator="['discount.full_order_price', { rules: [{ required: true, message: '抵扣条件不能为空' }] }]"
               />
             </a-form-item>
@@ -81,7 +84,7 @@
               <InputNumberGroup
                 addonBefore="最高可抵扣金额"
                 addonAfter="%"
-                :inputProps="{ min: 0.01 }"
+                :inputProps="{ min: 0.1, max: 90, precision: 1 }"
                 v-decorator="['discount.max_money_ratio', { rules: [{ required: true, message: '最高可抵扣金额不能为空' }] }]"
               />
               <div class="form-item-help">

+ 268 - 0
src/views/order/tools/Export.vue

@@ -0,0 +1,268 @@
+<template>
+  <div class="container">
+    <a-card class="mb-20" :bordered="false">
+      <div class="card-title">{{ $route.meta.title }}</div>
+      <a-alert class="mb-20" :showIcon="false" message="功能说明:此功能用于订单数据导出,导出后的文件请用Excel打开查看" banner />
+      <a-form class="my-form" :form="myForm" layout="inline" @submit="handleSubmit">
+        <a-row :gutter="24">
+          <a-col :span="12">
+            <a-form-item label="下单时间">
+              <a-range-picker
+                format="YYYY-MM-DD"
+                v-decorator="['betweenTime', { rules: [{ required: true, message: '请选择下单时间' }] }]"
+              />
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="订单来源">
+              <a-select v-decorator="['orderSource', { initialValue: -1 }]">
+                <a-select-option :value="-1">全部</a-select-option>
+                <a-select-option
+                  v-for="(item, index) in OrderSourceEnum.data"
+                  :key="index"
+                  :value="item.value"
+                >{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+          <a-col :span="12">
+            <a-form-item label="支付方式">
+              <a-select
+                v-decorator="['payType', { initialValue: -1, rules: [{ required: true, message: '请选择支付方式' }] } ]"
+              >
+                <a-select-option :value="-1">全部</a-select-option>
+                <a-select-option
+                  v-for="(item, index) in PayTypeEnum.data"
+                  :key="index"
+                  :value="item.value"
+                >{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+
+          <a-col :span="12">
+            <a-form-item label="配送方式">
+              <a-select v-decorator="['deliveryType', { initialValue: -1 }]">
+                <a-select-option :value="-1">全部</a-select-option>
+                <a-select-option
+                  v-for="(item, index) in DeliveryTypeEnum.data"
+                  :key="index"
+                  :value="item.value"
+                >{{ item.name }}</a-select-option>
+              </a-select>
+            </a-form-item>
+          </a-col>
+
+          <a-col :span="24">
+            <a-form-item class="item-main" label="导出的字段">
+              <a-checkbox-group
+                v-decorator="['columns', { initialValue: columnValue, rules: [{ required: true, message: '请选择导出的字段' }] }]"
+                :options="columnData"
+              />
+            </a-form-item>
+          </a-col>
+
+          <a-col :span="24">
+            <a-form-item class="form-btn">
+              <a-button
+                class="btn-main"
+                type="primary"
+                icon="download"
+                html-type="submit"
+                :loading="isBtnLoading"
+              >导出</a-button>
+              <a-button @click="handleReset">重置</a-button>
+            </a-form-item>
+          </a-col>
+        </a-row>
+      </a-form>
+    </a-card>
+    <a-card :bordered="false">
+      <div class="card-title">历史导出记录</div>
+      <ExportList ref="ExportList" />
+    </a-card>
+  </div>
+</template>
+
+<script>
+import { inArray, assignment } from '@/utils/util'
+import * as Api from '@/api/order/export'
+import { DeliveryTypeEnum, OrderSourceEnum, PayTypeEnum, OrderStatusEnum } from '@/common/enum/order'
+import { ExportList } from './modules'
+
+// 订单字段记录
+const columnData = [
+  { label: '订单ID', value: 'order_id' },
+  { label: '订单号', value: 'order_no' },
+  { label: '商品信息', value: 'goods_detail' },
+  { label: '商品总额', value: 'total_price' },
+  { label: '优惠券抵扣金额', value: 'coupon_money' },
+  { label: '积分抵扣金额', value: 'points_money' },
+  { label: '后台改价', value: 'update_price' },
+  { label: '运费金额', value: 'express_price' },
+  { label: '订单实付款', value: 'pay_price' },
+  { label: '支付方式', value: 'pay_type' },
+  { label: '下单时间', value: 'create_time' },
+  { label: '买家信息', value: 'user_info' },
+  { label: '买家留言', value: 'buyer_remark' },
+  { label: '配送方式', value: 'delivery_type' },
+  { label: '收货人', value: 'receipt_name' },
+  { label: '联系电话', value: 'receipt_phone' },
+  { label: '收货地址', value: 'receipt_address' },
+  { label: '物流公司', value: 'express_company' },
+  { label: '物流单号', value: 'express_no' },
+  { label: '付款状态', value: 'pay_status' },
+  { label: '付款时间', value: 'pay_time' },
+  { label: '发货状态', value: 'delivery_status' },
+  { label: '发货时间', value: 'delivery_time' },
+  { label: '收货状态', value: 'receipt_status' },
+  { label: '收货时间', value: 'receipt_time' },
+  { label: '订单状态', value: 'order_status' },
+  { label: '是否已评价', value: 'is_comment' },
+  { label: '订单来源', value: 'order_source' },
+]
+
+// 默认选中的字段
+const columnValue = [
+  'order_no'
+  , 'goods_detail'
+  , 'total_price'
+  , 'coupon_money'
+  , 'points_money'
+  , 'update_price'
+  , 'express_price'
+  , 'pay_price'
+  , 'pay_type'
+  , 'create_time'
+  , 'user_info'
+  , 'buyer_remark'
+  , 'delivery_type'
+  , 'receipt_name'
+  , 'receipt_phone'
+  , 'receipt_address'
+  , 'express_company'
+  , 'express_no'
+  , 'pay_status'
+  , 'pay_time'
+  , 'delivery_status'
+  , 'delivery_time'
+  , 'receipt_status'
+  , 'receipt_time'
+  , 'order_status'
+  , 'order_source']
+
+export default {
+  name: 'Export',
+  components: {
+    ExportList
+  },
+  data () {
+    return {
+      // 当前表单元素
+      myForm: this.$form.createForm(this),
+      // 正在加载
+      isLoading: false,
+      isBtnLoading: false,
+      // 标签布局属性
+      labelCol: { span: 8 },
+      // 输入框布局属性
+      wrapperCol: { span: 16 },
+      // 订单字段数据
+      columnData,
+      columnValue,
+    }
+  },
+  beforeCreate () {
+    // 批量给当前实例赋值
+    assignment(this, {
+      inArray,
+      DeliveryTypeEnum,
+      OrderSourceEnum,
+      OrderStatusEnum,
+      PayTypeEnum,
+    })
+
+  },
+
+  methods: {
+
+    // 表单提交
+    handleSubmit (e) {
+      e.preventDefault()
+      this.myForm.validateFields((error, values) => {
+        if (!error) {
+          this.onFormSubmit(values)
+        }
+      })
+    },
+
+    // 提交到后端api
+    onFormSubmit (data) {
+      const app = this
+      app.isLoading = true
+      app.isBtnLoading = true
+      Api.exportOrder(data)
+        .then(result => {
+          // 显示提示信息
+          app.$message.success(result.message, 1.5)
+          // 刷新历史导出记录
+          app.$refs.ExportList.handleRefresh()
+        })
+        .finally(result => {
+          app.isLoading = false
+          app.isBtnLoading = false
+        })
+    },
+
+    // 重置搜索表单
+    handleReset () {
+      this.myForm.resetFields()
+    },
+
+  }
+
+}
+</script>
+
+<style lang="less" scoped>
+// 表单样式
+.my-form {
+  width: 980px;
+  padding-left: 15px;
+
+  /deep/.ant-form-item {
+    margin-bottom: 0;
+    min-height: 57px;
+
+    .ant-form-item-label {
+      width: 85px;
+      margin-right: 15px;
+      text-align: right;
+    }
+    .ant-form-item-control-wrapper {
+      min-width: 220px;
+    }
+
+    &.item-main {
+      .ant-form-item-control-wrapper {
+        width: 805px;
+        padding-top: 12px;
+      }
+    }
+
+    &.form-btn {
+      margin-top: 12px;
+      width: auto;
+      .btn-main {
+        margin-right: 10px;
+      }
+    }
+  }
+
+  // 多选框元素
+  /deep/.ant-checkbox-group-item {
+    width: 160px;
+    margin-bottom: 12px;
+  }
+}
+</style>

+ 110 - 0
src/views/order/tools/modules/ExportList.vue

@@ -0,0 +1,110 @@
+<template>
+  <s-table
+    ref="table"
+    rowKey="id"
+    :loading="isLoading"
+    :columns="columns"
+    :data="loadData"
+    :pageSize="10"
+  >
+    <!-- 下单时间 -->
+    <template slot="time" slot-scope="item">
+      <span>{{ item.start_time }}</span>
+      <span class="ml-5 mr-5">到</span>
+      <span>{{ item.end_time }}</span>
+    </template>
+    <!-- 导出状态 -->
+    <template slot="status" slot-scope="text">
+      <a-tag :color="ExportStatusColorEnum[text]">{{ ExportStatusEnum[text].name }}</a-tag>
+    </template>
+    <!-- 操作 -->
+    <span class="actions" slot="action" slot-scope="text, item">
+      <a
+        v-if="item.status === ExportStatusEnum.COMPLETED.value"
+        v-action:download
+        :href="item.download_url"
+        target="_blank"
+      >下载</a>
+    </span>
+  </s-table>
+</template>
+
+<script>
+import { STable } from '@/components'
+import * as Api from '@/api/order/export'
+import { ExportStatusEnum } from '@/common/enum/order/export'
+
+const ExportStatusColorEnum = {
+  [ExportStatusEnum.NORMAL.value]: '',
+  [ExportStatusEnum.COMPLETED.value]: 'green',
+  [ExportStatusEnum.FAIL.value]: 'red',
+}
+
+// 订单导出记录组件
+export default {
+  name: 'ExportList',
+  components: {
+    STable
+  },
+
+  data () {
+    return {
+      // 正在加载
+      isLoading: false,
+      // 枚举类
+      ExportStatusEnum,
+      ExportStatusColorEnum,
+      // 表头
+      columns: [
+        {
+          title: '记录ID',
+          dataIndex: 'id'
+        },
+        {
+          title: '下单时间',
+          scopedSlots: { customRender: 'time' }
+        },
+        {
+          title: '导出状态',
+          dataIndex: 'status',
+          scopedSlots: { customRender: 'status' }
+        },
+        {
+          title: '操作',
+          width: '180px',
+          scopedSlots: { customRender: 'action' }
+        }
+      ],
+      // 加载数据方法 必须为 Promise 对象
+      loadData: param => {
+        return Api.list(param).then(response => response.data.list)
+      }
+    }
+  },
+  watch: {
+    value: {
+      // 首次加载的时候执行函数
+      immediate: true,
+      handler (val) {
+        this.imgUrl = val
+      }
+    }
+  },
+  created () {
+  },
+  methods: {
+
+    /**
+     * 刷新列表
+     * @param Boolean bool 强制刷新到第一页
+     */
+    handleRefresh (bool = false) {
+      this.$refs.table.refresh(bool)
+    },
+
+  }
+}
+</script>
+
+<style lang="less" scoped>
+</style>

+ 3 - 0
src/views/order/tools/modules/index.js

@@ -0,0 +1,3 @@
+import ExportList from './ExportList'
+
+export { ExportList }

+ 77 - 13
src/views/setting/Sms.vue

@@ -3,20 +3,28 @@
     <div class="card-title">{{ $route.meta.title }}</div>
     <a-spin :spinning="isLoading">
       <a-form :form="form" @submit="handleSubmit">
-        <a-form-item label="短信平台" :labelCol="labelCol" :wrapperCol="wrapperCol">
-          <a-radio-group v-decorator="['default', { rules: [{ required: true }] }]">
-            <a-radio value="aliyun">阿里云短信</a-radio>
+        <a-form-item class="mb-20" label="短信平台" :labelCol="labelCol" :wrapperCol="wrapperCol">
+          <a-radio-group
+            v-decorator="['default', { rules: [{ required: true }] }]"
+            @change="onChangeEngine"
+          >
+            <a-radio
+              v-for="(engine, index) in record.engine"
+              :key="index"
+              :value="index"
+            >{{ engine.name }}</a-radio>
           </a-radio-group>
-          <div class="form-item-help">
-            <small style="margin-right: 6px;">短信服务管理控制台:</small>
+
+          <div v-if="form.getFieldValue('default')" class="form-item-help">
+            <small>短信平台管理地址:</small>
             <a
-              href="https://dysms.console.aliyun.com/dysms.htm"
+              :href="record.engine[form.getFieldValue('default')].website"
               target="_blank"
-            >https://dysms.console.aliyun.com/dysms.htm</a>
+            >{{ record.engine[form.getFieldValue('default')].website }}</a>
           </div>
         </a-form-item>
         <!-- 阿里云配置 -->
-        <div v-show="form.getFieldValue('default') == 'aliyun'">
+        <div v-show="form.getFieldValue('default') === 'aliyun'">
           <a-form-item label="AccessKeyId" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
             <a-input v-decorator="[`engine.aliyun.AccessKeyId`]" />
           </a-form-item>
@@ -32,6 +40,35 @@
             <a-input v-decorator="[`engine.aliyun.sign`]" />
           </a-form-item>
         </div>
+        <!-- 腾讯云配置 -->
+        <div v-show="form.getFieldValue('default') === 'qcloud'">
+          <a-form-item label="SdkAppID" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
+            <a-input v-decorator="[`engine.qcloud.SdkAppID`]" />
+          </a-form-item>
+          <a-form-item label="AccessKeyId" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
+            <a-input v-decorator="[`engine.qcloud.AccessKeyId`]" />
+          </a-form-item>
+          <a-form-item
+            label="AccessKeySecret"
+            :labelCol="labelCol"
+            :wrapperCol="wrapperCol"
+            required
+          >
+            <a-input v-decorator="[`engine.qcloud.AccessKeySecret`]" />
+          </a-form-item>
+          <a-form-item label="短信签名 Sign" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
+            <a-input v-decorator="[`engine.qcloud.sign`]" />
+          </a-form-item>
+        </div>
+        <!-- 七牛云配置 -->
+        <div v-show="form.getFieldValue('default') === 'qiniu'">
+          <a-form-item label="AccessKey" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
+            <a-input v-decorator="[`engine.qiniu.AccessKey`]" />
+          </a-form-item>
+          <a-form-item label="SecretKey" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
+            <a-input v-decorator="[`engine.qiniu.SecretKey`]" />
+          </a-form-item>
+        </div>
 
         <!-- 短信场景配置 -->
         <div v-for="(item, index) in record['scene']" :key="index">
@@ -45,7 +82,7 @@
             </a-radio-group>
           </a-form-item>
           <a-form-item label="模板内容" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
-            <span>{{ record.scene[index].content }}</span>
+            <span>{{ record.scene[index].contentPractical }}</span>
           </a-form-item>
           <a-form-item label="模板ID/Code" :labelCol="labelCol" :wrapperCol="wrapperCol" required>
             <a-input v-decorator="[`scene.${index}.templateCode`]" />
@@ -78,7 +115,7 @@
 </template>
 
 <script>
-import pick from 'lodash.pick'
+import { pick, omit } from 'lodash'
 import { isEmpty } from '@/utils/util'
 import * as Api from '@/api/setting/store'
 import SettingSmsSceneEnum from '@/common/enum/setting/sms/Scene'
@@ -123,19 +160,46 @@ export default {
         })
     },
 
+    // 切换短信平台事件
+    onChangeEngine (e) {
+      const app = this
+      const engine = e.target.value
+      for (const index in app.record.scene) {
+        const item = app.record.scene[index]
+        item.contentPractical = app.onVsprintf(item.content, item.variables[engine])
+      }
+    },
+
+    // 解析短信内容变量, 生成完整的模板内容
+    onVsprintf (str, variables) {
+      const reg = new RegExp('%s')
+      for (var i = 0; i < variables.length; i++) {
+        str = str.replace(reg, variables[i])
+      }
+      return str
+    },
+
     /**
      * 设置默认值
      */
     setFieldsValue () {
-      const { record, $nextTick, form } = this
+      const app = this
+      const { record, $nextTick, form } = app
       !isEmpty(form.getFieldsValue()) && $nextTick(() => {
         const scene = {}
         for (const index in record.scene) {
-          scene[index] = pick(record.scene[index], ['isEnable', 'templateCode', 'acceptPhone'])
+          const item = record.scene[index]
+          const contentPractical = app.onVsprintf(item.content, item.variables[record.default])
+          app.$set(item, 'contentPractical', contentPractical)
+          scene[index] = pick(item, ['isEnable', 'templateCode', 'acceptPhone'])
+        }
+        const engine = {}
+        for (const index in record.engine) {
+          engine[index] = omit(record.engine[index], ['name', 'website'])
         }
         form.setFieldsValue({
           default: record.default,
-          engine: record.engine,
+          engine,
           scene
         })
       })

+ 4 - 3
src/views/user/Index.vue

@@ -9,7 +9,7 @@
             <a-input v-decorator="['search']" placeholder="请输入昵称/手机号" />
           </a-form-item>
           <a-form-item label="会员等级">
-            <a-select placeholder="请选择会员等级" v-model="queryParam.grade_id">
+            <a-select v-decorator="['gradeId', { initialValue: 0 }]">
               <a-select-option :value="0">全部</a-select-option>
               <a-select-option
                 v-for="(item, index) in gradeList"
@@ -92,7 +92,7 @@ import * as Api from '@/api/user'
 import * as GradeApi from '@/api/user/grade'
 import { STable } from '@/components'
 import { GradeForm, RechargeForm } from './modules'
-import { mpweixin, h5 } from '@/core/icons'
+import { mpweixin, h5, app } from '@/core/icons'
 
 // 注册来源名称
 const PlatformName = {
@@ -104,7 +104,8 @@ const PlatformName = {
 // 注册来源图标
 const PlatformIcons = {
   'MP-WEIXIN': mpweixin,
-  'H5': h5
+  'H5': h5,
+  'APP': app
 }
 
 export default {

+ 1 - 1
version.json

@@ -1,3 +1,3 @@
 {
-  "version": "2.0.3"
+  "version": "2.0.4"
 }