wangxy hai 15 horas
pai
achega
3a535eee66

+ 18 - 0
src/api/hisStore/guCoinGoods.js

@@ -0,0 +1,18 @@
+import request from '@/utils/request'
+
+// 查询谷币商品列表(只读)
+export function listGuCoinGoods(query) {
+  return request({
+    url: '/his/guCoinGoods/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询谷币商品详细
+export function getGuCoinGoods(goodsId) {
+  return request({
+    url: '/his/guCoinGoods/' + goodsId,
+    method: 'get'
+  })
+}

+ 53 - 0
src/api/hisStore/guCoinOrder.js

@@ -0,0 +1,53 @@
+import request from '@/utils/request'
+
+// 查询谷币订单列表
+export function listGuCoinOrder(query) {
+  return request({
+    url: '/his/guCoinOrder/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询谷币订单详细
+export function getGuCoinOrder(orderId) {
+  return request({
+    url: '/his/guCoinOrder/' + orderId,
+    method: 'get'
+  })
+}
+
+// 发货
+export function sendGuCoinGoods(data) {
+  return request({
+    url: '/his/guCoinOrder/sendGoods',
+    method: 'put',
+    data: data
+  })
+}
+
+// 修改谷币订单
+export function updateGuCoinOrder(data) {
+  return request({
+    url: '/his/guCoinOrder',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除谷币订单
+export function delGuCoinOrder(orderIds) {
+  return request({
+    url: '/his/guCoinOrder/' + orderIds,
+    method: 'delete'
+  })
+}
+
+// 销售代下谷币订单(多商品合并一单)
+export function createGuCoinOrderManually(data) {
+  return request({
+    url: '/his/guCoinOrder/createByManually',
+    method: 'post',
+    data: data
+  })
+}

+ 19 - 0
src/api/hisStore/userGuCoin.js

@@ -0,0 +1,19 @@
+import request from '@/utils/request'
+
+// 销售后台录入会员谷币
+export function addGuCoin(data) {
+  return request({
+    url: '/his/userGuCoin/addGuCoin',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询会员谷币记录列表
+export function listGuCoinLogs(query) {
+  return request({
+    url: '/his/userGuCoin/list',
+    method: 'get',
+    params: query
+  })
+}

+ 183 - 0
src/views/hisStore/guCoinGoods/index.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="商品名称" prop="goodsName">
+        <el-input
+          v-model="queryParams.goodsName"
+          placeholder="请输入商品名称"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="商品分类" prop="goodsType">
+        <el-select v-model="queryParams.goodsType" placeholder="请选择商品分类" clearable size="small">
+          <el-option
+            v-for="dict in goodsTypeOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" border :data="guCoinGoodsList">
+      <el-table-column label="封面图" align="center" prop="imgUrl" width="120">
+        <template slot-scope="scope">
+          <el-popover placement="right" title="" trigger="hover">
+            <img slot="reference" :src="scope.row.imgUrl" width="80">
+            <img :src="scope.row.imgUrl" style="max-width: 150px;">
+          </el-popover>
+        </template>
+      </el-table-column>
+      <el-table-column label="商品名称" align="center" prop="goodsName" show-overflow-tooltip />
+      <el-table-column label="原价" align="center" prop="otPrice" />
+      <el-table-column label="商品分类" align="center" prop="goodsType">
+        <template slot-scope="scope">
+          <dict-tag :options="goodsTypeOptions" :value="scope.row.goodsType"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :options="statusOptions" :value="scope.row.status"/>
+        </template>
+      </el-table-column>
+      <el-table-column label="所需谷币" align="center" prop="guCoin" />
+      <el-table-column label="需支付金额" align="center" prop="cash" />
+      <el-table-column label="库存" align="center" prop="stock" />
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180"/>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="100">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleDetail(scope.row)"
+          >详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 商品详情对话框 -->
+    <el-dialog title="谷币商品详情" :visible.sync="detailOpen" width="700px" append-to-body>
+      <el-descriptions :column="2" border v-if="detailData">
+        <el-descriptions-item label="商品名称" :span="2">{{ detailData.goodsName }}</el-descriptions-item>
+        <el-descriptions-item label="商品分类">
+          <dict-tag :options="goodsTypeOptions" :value="detailData.goodsType"/>
+        </el-descriptions-item>
+        <el-descriptions-item label="状态">
+          <dict-tag :options="statusOptions" :value="detailData.status"/>
+        </el-descriptions-item>
+        <el-descriptions-item label="原价">{{ detailData.otPrice }}</el-descriptions-item>
+        <el-descriptions-item label="商品编号">{{ detailData.barCode }}</el-descriptions-item>
+        <el-descriptions-item label="所需谷币">{{ detailData.guCoin }}</el-descriptions-item>
+        <el-descriptions-item label="需支付金额">{{ detailData.cash }}</el-descriptions-item>
+        <el-descriptions-item label="排序">{{ detailData.sort }}</el-descriptions-item>
+        <el-descriptions-item label="库存">{{ detailData.stock }}</el-descriptions-item>
+        <el-descriptions-item label="封面图" :span="2">
+          <el-image v-if="detailData.imgUrl" :src="detailData.imgUrl" style="width: 120px;" :preview-src-list="[detailData.imgUrl]"></el-image>
+        </el-descriptions-item>
+        <el-descriptions-item label="轮播图" :span="2">
+          <el-image v-for="(img, idx) in photoArr" :key="idx" :src="img" style="width: 80px; margin-right: 5px;" :preview-src-list="photoArr"></el-image>
+        </el-descriptions-item>
+        <el-descriptions-item label="商品详情" :span="2">
+          <div v-html="detailData.descs" style="max-height: 300px; overflow-y: auto;"></div>
+        </el-descriptions-item>
+      </el-descriptions>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listGuCoinGoods, getGuCoinGoods } from "@/api/hisStore/guCoinGoods";
+
+export default {
+  name: "GuCoinGoods",
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      guCoinGoodsList: [],
+      detailOpen: false,
+      detailData: null,
+      photoArr: [],
+      goodsTypeOptions: [],
+      statusOptions: [],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        goodsName: null,
+        goodsType: null,
+        status: null
+      }
+    };
+  },
+  created() {
+    this.getList();
+    this.getDicts("sys_integral_goods_type").then(response => {
+      this.goodsTypeOptions = response.data;
+    });
+    this.getDicts("sys_company_status").then(response => {
+      this.statusOptions = response.data;
+    });
+  },
+  methods: {
+    getList() {
+      this.loading = true;
+      listGuCoinGoods(this.queryParams).then(response => {
+        this.guCoinGoodsList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    handleDetail(row) {
+      getGuCoinGoods(row.goodsId).then(response => {
+        this.detailData = response.data;
+        this.photoArr = [];
+        if (this.detailData.images) {
+          this.photoArr = this.detailData.images.split(',').filter(s => s);
+        }
+        this.detailOpen = true;
+      });
+    }
+  }
+};
+</script>

+ 262 - 0
src/views/hisStore/guCoinOrder/index.vue

@@ -0,0 +1,262 @@
+<template>
+  <div class="app-container">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="80px">
+      <el-form-item label="订单编号" prop="orderCode">
+        <el-input
+          v-model="queryParams.orderCode"
+          placeholder="请输入订单编号"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="会员电话" prop="userPhone">
+        <el-input
+          v-model="queryParams.userPhone"
+          placeholder="请输入会员电话"
+          clearable
+          size="small"
+          @keyup.enter.native="handleQuery"
+        />
+      </el-form-item>
+      <el-form-item label="支付状态" prop="isPay">
+        <el-select v-model="queryParams.isPay" placeholder="请选择支付状态" clearable size="small">
+          <el-option label="未支付" :value="0" />
+          <el-option label="已支付" :value="1" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="订单状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择订单状态" clearable size="small">
+          <el-option
+            v-for="item in statusOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <el-tabs type="card" v-model="actName" @tab-click="handleClickX">
+      <el-tab-pane label="全部订单" name="10"></el-tab-pane>
+      <el-tab-pane v-for="item in statusOptions" :key="item.value" :label="item.label" :name="String(item.value)"></el-tab-pane>
+    </el-tabs>
+
+    <el-table v-loading="loading" border :data="guCoinOrderList">
+      <el-table-column label="订单编号" align="center" prop="orderCode" width="200" />
+      <el-table-column label="会员名称" align="center" prop="userName" />
+      <el-table-column label="会员电话" align="center" prop="userPhone" width="120" />
+      <el-table-column label="收货地址" align="center" prop="userAddress" show-overflow-tooltip />
+      <el-table-column label="支付谷币" align="center" prop="guCoin" />
+      <el-table-column label="支付金额" align="center" prop="payMoney">
+        <template slot-scope="scope">
+          <span v-if="scope.row.payMoney != null">{{ Number(scope.row.payMoney).toFixed(2) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="支付类型" align="center" prop="payType" width="100">
+        <template slot-scope="scope">
+          <span>{{ payTypeText(scope.row.payType) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="支付状态" align="center" prop="isPay" width="90">
+        <template slot-scope="scope">
+          <el-tag :type="scope.row.isPay === 1 ? 'success' : 'info'" size="mini">
+            {{ scope.row.isPay === 1 ? '已支付' : '未支付' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="订单状态" align="center" prop="status" width="100">
+        <template slot-scope="scope">
+          <el-tag :type="statusTagType(scope.row.status)" size="mini">
+            {{ statusText(scope.row.status) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="快递名称" align="center" prop="deliveryName" />
+      <el-table-column label="快递单号" align="center" prop="deliverySn" width="160" />
+      <el-table-column label="发货时间" align="center" prop="deliveryTime" width="160" />
+      <el-table-column label="下单时间" align="center" prop="createTime" width="160" />
+      <el-table-column label="备注" align="center" prop="remark" show-overflow-tooltip />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="100">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" @click="handleDetail(scope.row)">详情</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 订单详情对话框 -->
+    <el-dialog title="谷币订单详情" :visible.sync="detailOpen" width="800px" append-to-body>
+      <el-descriptions :column="2" border v-if="detailData">
+        <el-descriptions-item label="订单编号">{{ detailData.orderCode }}</el-descriptions-item>
+        <el-descriptions-item label="订单状态">
+          <el-tag :type="statusTagType(detailData.status)" size="mini">{{ statusText(detailData.status) }}</el-tag>
+        </el-descriptions-item>
+        <el-descriptions-item label="会员名称">{{ detailData.userName }}</el-descriptions-item>
+        <el-descriptions-item label="会员电话">{{ detailData.userPhone }}</el-descriptions-item>
+        <el-descriptions-item label="收货地址" :span="2">{{ detailData.userAddress }}</el-descriptions-item>
+        <el-descriptions-item label="订单总谷币">{{ detailData.totalGuCoin }}</el-descriptions-item>
+        <el-descriptions-item label="折扣谷币">{{ detailData.discountGuCoin }}</el-descriptions-item>
+        <el-descriptions-item label="实付谷币">{{ detailData.guCoin }}</el-descriptions-item>
+        <el-descriptions-item label="订单总金额">{{ detailData.totalMoney }}</el-descriptions-item>
+        <el-descriptions-item label="折扣金额">{{ detailData.discountMoney }}</el-descriptions-item>
+        <el-descriptions-item label="实付金额">{{ detailData.payMoney }}</el-descriptions-item>
+        <el-descriptions-item label="运费">{{ detailData.deliveryMoney }}</el-descriptions-item>
+        <el-descriptions-item label="支付类型">{{ payTypeText(detailData.payType) }}</el-descriptions-item>
+        <el-descriptions-item label="支付状态">{{ detailData.isPay === 1 ? '已支付' : '未支付' }}</el-descriptions-item>
+        <el-descriptions-item label="支付时间">{{ detailData.payTime }}</el-descriptions-item>
+        <el-descriptions-item label="快递名称">{{ detailData.deliveryName }}</el-descriptions-item>
+        <el-descriptions-item label="快递单号">{{ detailData.deliverySn }}</el-descriptions-item>
+        <el-descriptions-item label="发货时间">{{ detailData.deliveryTime }}</el-descriptions-item>
+        <el-descriptions-item label="下单时间">{{ detailData.createTime }}</el-descriptions-item>
+        <el-descriptions-item label="销售ID">{{ detailData.companyUserId }}</el-descriptions-item>
+        <el-descriptions-item label="销售公司ID">{{ detailData.companyId }}</el-descriptions-item>
+        <el-descriptions-item label="企业微信ID">{{ detailData.qwUserId }}</el-descriptions-item>
+        <el-descriptions-item label="备注" :span="2">{{ detailData.remark }}</el-descriptions-item>
+      </el-descriptions>
+
+      <div style="margin-top: 15px;" v-if="itemList.length > 0">
+        <div style="font-weight: bold; margin-bottom: 8px;">商品明细</div>
+        <el-table border :data="itemList" size="small">
+          <el-table-column label="商品图片" align="center" width="90">
+            <template slot-scope="scope">
+              <el-popover v-if="scope.row.imgUrl" placement="right" title="" trigger="hover">
+                <img slot="reference" :src="scope.row.imgUrl" width="50">
+                <img :src="scope.row.imgUrl" style="max-width: 150px">
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品名称" align="center" prop="goodsName" show-overflow-tooltip />
+          <el-table-column label="所需谷币" align="center" prop="guCoin" />
+          <el-table-column label="需支付金额" align="center" prop="cash">
+            <template slot-scope="scope">
+              <span v-if="scope.row.cash != null">{{ Number(scope.row.cash).toFixed(2) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="数量" align="center" prop="num" />
+        </el-table>
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailOpen = false">关 闭</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listGuCoinOrder, getGuCoinOrder } from "@/api/hisStore/guCoinOrder";
+
+export default {
+  name: "GuCoinOrder",
+  data() {
+    return {
+      loading: true,
+      showSearch: true,
+      total: 0,
+      guCoinOrderList: [],
+      detailOpen: false,
+      detailData: null,
+      itemList: [],
+      actName: "10",
+      // 1待发货 2待收货 3已完成 4待支付 -1取消
+      statusOptions: [
+        { value: 4, label: "待支付" },
+        { value: 1, label: "待发货" },
+        { value: 2, label: "待收货" },
+        { value: 3, label: "已完成" },
+        { value: -1, label: "取消" }
+      ],
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        orderCode: null,
+        userPhone: null,
+        status: null,
+        isPay: null
+      }
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    getList() {
+      this.loading = true;
+      listGuCoinOrder(this.queryParams).then(response => {
+        this.guCoinOrderList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.actName = "10";
+      this.handleQuery();
+    },
+    handleClickX(tab) {
+      if (tab.name === "10") {
+        this.queryParams.status = null;
+      } else {
+        this.queryParams.status = Number(tab.name);
+      }
+      this.handleQuery();
+    },
+    statusText(status) {
+      const item = this.statusOptions.find(d => d.value === status);
+      return item ? item.label : "未知";
+    },
+    statusTagType(status) {
+      switch (status) {
+        case 4: return "warning";
+        case 1: return "warning";
+        case 2: return "primary";
+        case 3: return "success";
+        case -1: return "info";
+        default: return "info";
+      }
+    },
+    payTypeText(payType) {
+      switch (payType) {
+        case 1: return "谷币";
+        case 2: return "现金";
+        case 3: return "谷币+现金";
+        default: return "-";
+      }
+    },
+    handleDetail(row) {
+      getGuCoinOrder(row.orderId).then(response => {
+        this.detailData = response.data;
+        this.itemList = [];
+        if (this.detailData && this.detailData.itemJson) {
+          try {
+            this.itemList = JSON.parse(this.detailData.itemJson) || [];
+          } catch (e) {
+            this.itemList = [];
+          }
+        }
+        this.detailOpen = true;
+      });
+    }
+  }
+};
+</script>

+ 112 - 4
src/views/store/financeOrder/index.vue

@@ -123,10 +123,23 @@
           v-hasPermi="['store:financeOrder:export']"
         >导出</el-button>
       </el-col>
+      <el-col :span="1.5">
+        <el-button
+          type="danger"
+          plain
+          icon="el-icon-refresh-left"
+          size="mini"
+          :disabled="selectedRows.length === 0"
+          :loading="batchReturnLoading"
+          @click="handleBatchReturnUnpaid"
+          v-hasPermi="['c']"
+        >转未回款</el-button>
+      </el-col>
       <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
     </el-row>
 
-    <el-table :key="tableKey" v-loading="loading" border :data="orderList" row-key="orderId" style="width:100%" show-summary :summary-method="getSummaries" @filter-change="handleFilterChange">
+    <el-table ref="orderTable" :key="tableKey" v-loading="loading" border :data="orderList" row-key="orderId" style="width:100%" show-summary :summary-method="getSummaries" @filter-change="handleFilterChange" @selection-change="handleSelectionChange">
+      <el-table-column type="selection" width="50" :reserve-selection="true" fixed="left" />
       <el-table-column label="订单编号" align="center" prop="orderCode" width="170" fixed="left" column-key="orderCode" :filters="getFilters('orderCode')" :filtered-value="columnFilters.orderCode" />
       <el-table-column label="订单日期" align="center" prop="externalCreateTime" width="160" column-key="externalCreateTime" :filters="getFilters('externalCreateTime')" :filtered-value="columnFilters.externalCreateTime">
         <template slot-scope="scope">
@@ -244,7 +257,15 @@
 
     <!-- 预览对话框 -->
     <el-dialog title="导入预览" :visible.sync="previewOpen" width="800px" append-to-body>
-      <el-table :data="previewList" border style="width:100%" max-height="400">
+      <el-alert
+        v-if="previewTotal > 0"
+        :title="previewSummaryText"
+        :type="previewFailCount > 0 ? 'warning' : 'success'"
+        :closable="false"
+        show-icon
+        style="margin-bottom: 10px;"
+      />
+      <el-table :data="previewList" border style="width:100%" max-height="400" show-summary :summary-method="getPreviewSummaries">
         <el-table-column label="快递单号" align="center" prop="deliverySn" width="180" />
         <el-table-column label="订单编号" align="center" prop="orderCode" width="170">
           <template slot-scope="scope">
@@ -275,6 +296,12 @@
         </el-table-column>
       </el-table>
       <div slot="footer" class="dialog-footer">
+        <el-button
+          v-if="previewFailCount > 0 && previewFailFile"
+          type="warning"
+          icon="el-icon-download"
+          @click="downloadPreviewFailFile"
+        >下载错误记录</el-button>
         <el-button type="primary" @click="confirmImport" :loading="previewLoading">确认导入</el-button>
         <el-button @click="previewOpen = false">取 消</el-button>
       </div>
@@ -283,7 +310,7 @@
 </template>
 
 <script>
-import { listFinanceOrder, editFinanceOrder, exportFinanceOrder, downloadFinanceImportTemplate, importFinanceOrder, previewImportFinanceOrder } from '@/api/store/financeOrder'
+import { listFinanceOrder, editFinanceOrder, exportFinanceOrder, downloadFinanceImportTemplate, importFinanceOrder, previewImportFinanceOrder, batchReturnUnpaidFinanceOrder } from '@/api/store/financeOrder'
 import { treeselect } from '@/api/company/companyDept'
 import Treeselect from '@riophae/vue-treeselect'
 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
@@ -353,7 +380,14 @@ export default {
       previewOpen: false,
       previewLoading: false,
       previewList: [],
+      previewTotal: 0,
+      previewSuccessCount: 0,
+      previewFailCount: 0,
+      previewFailFile: null,
       importFileRaw: null,
+      // 批量转未回款
+      selectedRows: [],
+      batchReturnLoading: false,
       columnFilters: {
         orderCode: [], externalCreateTime: [], returnTime: [], returnUser: [],
         userName: [], deliveryName: [], deliverySn: [], totalPrice: [],
@@ -369,6 +403,9 @@ export default {
     },
     returnStatusFilters() {
       return [{ text: '未回款', value: 0 }, { text: '已回款', value: 1 }];
+    },
+    previewSummaryText() {
+      return `共 ${this.previewTotal} 条记录:有效 ${this.previewSuccessCount} 条,无效 ${this.previewFailCount} 条`
     }
   },
   created() {
@@ -599,12 +636,21 @@ export default {
       formData.append('file', this.importFileRaw)
       this.importLoading = true
       previewImportFinanceOrder(formData).then(response => {
-        this.previewList = response.data || []
+        const data = response.data || {}
+        this.previewList = data.list || []
+        this.previewTotal = data.total || 0
+        this.previewSuccessCount = data.successCount || 0
+        this.previewFailCount = data.failCount || 0
+        this.previewFailFile = data.failFile || null
         this.$refs.upload.clearFiles()
         this.importDisableUpload = true
         this.importOpen = false
         this.previewOpen = true
         this.importLoading = false
+        // 有失败记录时弹出与正式导入一致的提示,引导用户下载错误记录
+        if (this.previewFailCount > 0 && response.msg) {
+          this.$message.warning(response.msg + ',可点击「下载错误记录」获取明细')
+        }
       }).catch(() => {
         this.importLoading = false
       })
@@ -639,6 +685,68 @@ export default {
       }).catch(() => {
         this.previewLoading = false
       })
+    },
+    /** 下载预览阶段的错误记录文件 */
+    downloadPreviewFailFile() {
+      if (!this.previewFailFile) {
+        this.msgError('暂无错误记录可下载')
+        return
+      }
+      this.download(this.previewFailFile)
+    },
+    /** 预览表格合计行:基于当前 previewList 求和(非分页,全部预览数据在内存中) */
+    getPreviewSummaries({ columns }) {
+      const sums = []
+      const sumProps = ['totalPrice', 'payPrice', 'collectionPrice', 'returnPrice']
+      columns.forEach((column, index) => {
+        if (index === 0) {
+          sums[index] = '合计'
+          return
+        }
+        if (sumProps.includes(column.property)) {
+          const total = this.previewList.reduce((acc, row) => {
+            const v = row[column.property]
+            return v != null ? acc + Number(v) : acc
+          }, 0)
+          sums[index] = isNaN(total) ? '' : total.toFixed(2)
+        } else {
+          sums[index] = ''
+        }
+      })
+      return sums
+    },
+    /** 表格多选变化(reserve-selection 已支持跨页保留) */
+    handleSelectionChange(selection) {
+      this.selectedRows = selection
+    },
+    /** 批量转未回款:将选中订单置为 status=3(已发货=未回款),不动 return_price */
+    handleBatchReturnUnpaid() {
+      if (this.selectedRows.length === 0) {
+        this.msgError('请至少选择一条订单')
+        return
+      }
+      const orderIds = this.selectedRows.map(r => r.orderId)
+      const tip = `确认将选中的 ${orderIds.length} 条订单置为「未回款」状态?\n(仅修改回款状态,不修改回款金额)`
+      this.$confirm(tip, '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        this.batchReturnLoading = true
+        return batchReturnUnpaidFinanceOrder(orderIds)
+      }).then(response => {
+        this.batchReturnLoading = false
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          // 清空选中(避免跨页选中残留)
+          this.$refs.orderTable && this.$refs.orderTable.clearSelection()
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
+      }).catch(() => {
+        this.batchReturnLoading = false
+      })
     }
   }
 }

+ 274 - 0
src/views/store/user/components/guCoinOrderDialog.vue

@@ -0,0 +1,274 @@
+<template>
+  <el-dialog title="创建谷币订单" :visible.sync="dialogVisible" width="1000px" append-to-body :close-on-click-modal="false" @close="handleClose">
+    <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+      <el-form-item label="收货信息" prop="addressId">
+        <el-row>
+          <el-col>
+            <el-button plain type="primary" icon="el-icon-plus" @click="handleAddUserAddress()">添加收货地址</el-button>
+          </el-col>
+        </el-row>
+        <el-radio-group v-model="form.addressId" style="width:100%">
+          <el-table border style="margin-top:5px;" v-loading="addressloading" :data="address">
+            <el-table-column label="ID" align="center" width="60">
+              <template slot-scope="scope">
+                <el-radio :label="scope.row.addressId"></el-radio>
+              </template>
+            </el-table-column>
+            <el-table-column label="收货人姓名" align="center" prop="realName" />
+            <el-table-column label="收货人所在省" align="center" prop="province" />
+            <el-table-column label="收货人所在市" align="center" prop="city" />
+            <el-table-column label="收货人所在区" align="center" prop="district" />
+            <el-table-column label="收货人详细地址" align="center" prop="detail" />
+          </el-table>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="商品信息" prop="goodsIds">
+        <div style="float: right;"><el-button plain type="primary" icon="el-icon-plus" @click="handleAddProduct">添加商品</el-button></div>
+        <el-table border width="100%" style="margin-top:5px;" :data="drugList">
+          <el-table-column label="商品图片" align="center" width="100">
+            <template slot-scope="scope">
+              <el-popover v-if="scope.row.imgUrl" placement="right" title="" trigger="hover">
+                <img slot="reference" :src="scope.row.imgUrl" width="50">
+                <img :src="scope.row.imgUrl" style="max-width: 150px">
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column label="商品名称" show-overflow-tooltip align="center" prop="goodsName" />
+          <el-table-column label="所需谷币" align="center" prop="guCoin" />
+          <el-table-column label="金额" align="center" prop="cash">
+            <template slot-scope="scope">
+              <span v-if="scope.row.cash!=null">{{ Number(scope.row.cash).toFixed(2) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="100px" fixed="right">
+            <template slot-scope="scope">
+              <el-button size="mini" type="text" icon="el-icon-delete" @click="handleProductDelete(scope.row)">删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-form-item>
+      <el-form-item label="合计">
+        <div>
+          <span style="margin-right: 30px;">谷币合计:<b style="color:#E6A23C;">{{ totalGuCoin }}</b></span>
+          <span>金额合计:<b style="color:#F56C6C;">{{ totalCash }}</b> 元</span>
+        </div>
+      </el-form-item>
+    </el-form>
+    <div slot="footer" class="dialog-footer">
+      <el-button type="primary" @click="submitForm">确 定</el-button>
+      <el-button @click="handleClose">取 消</el-button>
+    </div>
+
+    <el-dialog :title="userAddress.title" v-if="userAddress.open" :visible.sync="userAddress.open" width="800px" append-to-body>
+      <add-user-address ref="addUserAddress" @addUserAddress="addUserAddress" />
+    </el-dialog>
+
+    <el-dialog title="选择商品" :visible.sync="productDialogVisible" width="900px" append-to-body>
+      <el-row style="margin-bottom: 10px;">
+        <el-input v-model="productNameSearch" placeholder="请输入商品名称搜索" clearable size="small" style="width: 300px;" @keyup.enter.native="searchProductDialog" />
+        <el-button type="primary" size="small" style="margin-left: 10px;" @click="searchProductDialog">搜索</el-button>
+      </el-row>
+      <el-table border :data="productJson" @row-click="selectProduct" highlight-current-row style="cursor: pointer;">
+        <el-table-column label="商品图片" align="center" width="100">
+          <template slot-scope="scope">
+            <el-popover v-if="scope.row.imgUrl" placement="right" title="" trigger="hover">
+              <img slot="reference" :src="scope.row.imgUrl" width="50">
+              <img :src="scope.row.imgUrl" style="max-width: 150px">
+            </el-popover>
+          </template>
+        </el-table-column>
+        <el-table-column label="商品名称" show-overflow-tooltip align="center" prop="goodsName" />
+        <el-table-column label="所需谷币" align="center" prop="guCoin" />
+        <el-table-column label="金额" align="center" prop="cash">
+          <template slot-scope="scope">
+            <span v-if="scope.row.cash!=null">{{ Number(scope.row.cash).toFixed(2) }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="库存" align="center" prop="stock" />
+      </el-table>
+      <pagination
+        v-show="productTotal > 0"
+        :total="productTotal"
+        :page.sync="productQueryParams.pageNum"
+        :limit.sync="productQueryParams.pageSize"
+        @pagination="getProductList"
+      />
+    </el-dialog>
+  </el-dialog>
+</template>
+
+<script>
+import { listUserAddress } from "@/api/store/userAddress";
+import { listGuCoinGoods } from "@/api/hisStore/guCoinGoods";
+import { createGuCoinOrderManually } from "@/api/hisStore/guCoinOrder";
+import addUserAddress from "@/views/store/components/addUserAddress";
+
+export default {
+  name: "StoreGuCoinOrderDialog",
+  components: { addUserAddress },
+  props: {
+    userInfo: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      productDialogVisible: false,
+      productNameSearch: null,
+      productTotal: 0,
+      productQueryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        goodsName: null,
+        status: 1
+      },
+      form: {
+        addressId: null,
+        goodsIds: []
+      },
+      rules: {
+        addressId: [{ required: true, message: "请选择收货地址", trigger: "change" }]
+      },
+      address: [],
+      addressloading: false,
+      drugList: [],
+      productJson: [],
+      userAddress: {
+        open: false,
+        title: "创建收货地址"
+      }
+    };
+  },
+  computed: {
+    totalGuCoin() {
+      return this.drugList.reduce((sum, item) => sum + (Number(item.guCoin) || 0), 0);
+    },
+    totalCash() {
+      return this.drugList.reduce((sum, item) => sum + (Number(item.cash) || 0), 0).toFixed(2);
+    }
+  },
+  methods: {
+    init(userInfo) {
+      this.dialogVisible = true;
+      this.resetForm();
+      this.$nextTick(() => {
+        this.getAddressList(userInfo.userId);
+      });
+    },
+    resetForm() {
+      this.form = {
+        addressId: null,
+        goodsIds: []
+      };
+      this.drugList = [];
+      this.productJson = [];
+      this.productTotal = 0;
+      this.productNameSearch = null;
+      this.productQueryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        goodsName: null,
+        status: 1
+      };
+      this.productDialogVisible = false;
+      if (this.$refs.form) {
+        this.$refs.form.resetFields();
+      }
+    },
+    getAddressList(userId) {
+      var data = { userId: userId };
+      this.addressloading = true;
+      listUserAddress(data).then(response => {
+        this.address = response.rows;
+        this.addressloading = false;
+      });
+    },
+    handleAddProduct() {
+      this.productNameSearch = null;
+      this.productQueryParams.pageNum = 1;
+      this.productQueryParams.goodsName = null;
+      this.getProductList();
+      this.productDialogVisible = true;
+    },
+    searchProductDialog() {
+      this.productQueryParams.pageNum = 1;
+      this.productQueryParams.goodsName = this.productNameSearch;
+      this.getProductList();
+    },
+    getProductList() {
+      const data = {
+        pageNum: this.productQueryParams.pageNum,
+        pageSize: this.productQueryParams.pageSize,
+        goodsName: this.productQueryParams.goodsName,
+        status: 1
+      };
+      listGuCoinGoods(data).then(response => {
+        this.productJson = response.rows || [];
+        this.productTotal = response.total || 0;
+      });
+    },
+    selectProduct(row) {
+      for (var i = 0; i < this.drugList.length; i++) {
+        if (this.drugList[i].goodsId === row.goodsId) {
+          this.$message({ message: "该商品已添加", type: "warning" });
+          return;
+        }
+      }
+      if (row.stock != null && row.stock <= 0) {
+        this.$message({ message: "该商品库存不足", type: "warning" });
+        return;
+      }
+      this.drugList.push({ ...row });
+      this.syncGoodsIds();
+      this.$message({ message: "添加成功", type: "success" });
+    },
+    handleProductDelete(row) {
+      var index = this.drugList.findIndex(item => item.goodsId === row.goodsId);
+      if (index !== -1) {
+        this.drugList.splice(index, 1);
+      }
+      this.syncGoodsIds();
+    },
+    syncGoodsIds() {
+      this.form.goodsIds = this.drugList.map(item => item.goodsId);
+    },
+    handleAddUserAddress() {
+      this.userAddress.open = true;
+      setTimeout(() => {
+        this.$refs.addUserAddress.init(this.userInfo.userId);
+      }, 500);
+    },
+    addUserAddress() {
+      this.userAddress.open = false;
+      this.getAddressList(this.userInfo.userId);
+    },
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.goodsIds.length === 0) {
+            this.msgError("请选择至少一个商品");
+            return;
+          }
+          var submitData = {
+            userId: this.userInfo.userId,
+            addressId: this.form.addressId,
+            goodsIds: this.form.goodsIds
+          };
+          createGuCoinOrderManually(submitData).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("创建成功");
+              this.$emit("success");
+              this.handleClose();
+            }
+          });
+        }
+      });
+    },
+    handleClose() {
+      this.dialogVisible = false;
+    }
+  }
+};
+</script>

+ 101 - 6
src/views/store/user/myList.vue

@@ -105,6 +105,7 @@
       </el-table-column>
       <el-table-column label="手机号码" align="center" prop="phone" width="150px" />
       <el-table-column label="用户积分" align="center" prop="integral" />
+      <el-table-column label="谷币" align="center" prop="guCoin" />
       <el-table-column label="用户状态" align="center" prop="status" >
          <template slot-scope="scope">
               <dict-tag :options="userOptions" :value="scope.row.status"/>
@@ -119,7 +120,7 @@
           </el-tag>
         </template>
       </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="200px">
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" fixed="right" width="260px">
         <template slot-scope="scope">
 
           <el-button
@@ -138,6 +139,12 @@
             @click="handleCreateOrder(scope.row)"
             v-hasPermi="['store:packageOrder:add']"
           >去下单</el-button>
+          <el-button
+            size="mini"
+            type="text"
+            @click="handleAddGuCoin(scope.row)"
+            v-hasPermi="['his:userGuCoin:add']"
+          >添加谷币</el-button>
           <el-button
             size="mini"
             type="text"
@@ -190,10 +197,10 @@
          <userDetails  ref="userDetails" />
        </el-drawer>
 
-    <el-dialog title="选择订单类型" :visible.sync="orderTypeDialogVisible" width="600px" append-to-body>
+    <el-dialog title="选择订单类型" :visible.sync="orderTypeDialogVisible" width="760px" append-to-body>
       <el-radio-group v-model="selectedOrderType" style="width: 100%;">
         <el-row :gutter="20">
-          <el-col :span="8">
+          <el-col :span="6">
             <el-card shadow="hover" :class="{'selected-card': selectedOrderType === 'package'}" @click.native="selectedOrderType = 'package'">
               <div style="text-align: center; padding: 20px;">
                 <i class="el-icon-box" style="font-size: 32px; color: #409EFF;"></i>
@@ -201,7 +208,7 @@
               </div>
             </el-card>
           </el-col>
-          <el-col :span="8">
+          <el-col :span="6">
             <el-card shadow="hover" :class="{'selected-card': selectedOrderType === 'consultation'}" @click.native="selectedOrderType = 'consultation'">
               <div style="text-align: center; padding: 20px;">
                 <i class="el-icon-chat-dot-round" style="font-size: 32px; color: #67C23A;"></i>
@@ -209,7 +216,7 @@
               </div>
             </el-card>
           </el-col>
-          <el-col :span="8">
+          <el-col :span="6">
             <el-card shadow="hover" :class="{'selected-card': selectedOrderType === 'integral'}" @click.native="selectedOrderType = 'integral'">
               <div style="text-align: center; padding: 20px;">
                 <i class="el-icon-coin" style="font-size: 32px; color: #E6A23C;"></i>
@@ -217,6 +224,14 @@
               </div>
             </el-card>
           </el-col>
+          <el-col :span="6">
+            <el-card shadow="hover" :class="{'selected-card': selectedOrderType === 'guCoin'}" @click.native="selectedOrderType = 'guCoin'">
+              <div style="text-align: center; padding: 20px;">
+                <i class="el-icon-medal" style="font-size: 32px; color: #F56C6C;"></i>
+                <div style="margin-top: 10px; font-size: 14px; color: #303133;">谷养正道积分订单</div>
+              </div>
+            </el-card>
+          </el-col>
         </el-row>
       </el-radio-group>
       <div slot="footer" class="dialog-footer">
@@ -228,6 +243,32 @@
     <package-order-dialog ref="packageOrderDialog" :userInfo="currentOrderUser" @success="handleOrderSuccess" />
     <consultation-order-dialog ref="consultationOrderDialog" :userInfo="currentOrderUser" @success="handleOrderSuccess" />
     <integral-order-dialog ref="integralOrderDialog" :userInfo="currentOrderUser" @success="handleOrderSuccess" />
+    <gu-coin-order-dialog ref="guCoinOrderDialog" :userInfo="currentOrderUser" @success="handleOrderSuccess" />
+
+    <!-- 添加谷币对话框 -->
+    <el-dialog title="添加谷币" :visible.sync="addGuCoinOpen" width="500px" append-to-body>
+      <el-form ref="addGuCoinForm" :model="addGuCoinForm" :rules="addGuCoinRules" label-width="100px">
+        <el-form-item label="会员昵称">
+          <span>{{ addGuCoinForm.nickname }}</span>
+        </el-form-item>
+        <el-form-item label="当前谷币">
+          <span>{{ addGuCoinForm.currentGuCoin }}</span>
+        </el-form-item>
+        <el-form-item label="谷币数量" prop="guCoin">
+          <el-input-number v-model="addGuCoinForm.guCoin" :min="1" :precision="0" controls-position="right" placeholder="请输入谷币数量" style="width: 200px;" />
+        </el-form-item>
+        <el-form-item label="消费金额" prop="consumeAmount">
+          <el-input-number v-model="addGuCoinForm.consumeAmount" :min="0" :precision="2" controls-position="right" placeholder="请输入消费金额(选填)" style="width: 200px;" />
+        </el-form-item>
+        <el-form-item label="发放备注" prop="remark">
+          <el-input v-model="addGuCoinForm.remark" type="textarea" :rows="3" placeholder="请输入发放备注(选填)" maxlength="200" show-word-limit />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitAddGuCoin">确 定</el-button>
+        <el-button @click="addGuCoinOpen = false">取 消</el-button>
+      </div>
+    </el-dialog>
 
 
   </div>
@@ -239,12 +280,14 @@ import userDetails from '../components/userDetails.vue';
 import packageOrderDialog from './components/packageOrderDialog.vue';
 import consultationOrderDialog from './components/consultationOrderDialog.vue';
 import integralOrderDialog from './components/integralOrderDialog.vue';
+import guCoinOrderDialog from './components/guCoinOrderDialog.vue';
+import { addGuCoin } from "@/api/hisStore/userGuCoin";
 import { getSipPhoneNumber } from '@/api/user/fsUser'
 import { xorDecrypt } from '@/utils/util'
 import softPhone from '@/utils/SoftPhoneService'
 export default {
   name: "User",
-  components: {userDetails, packageOrderDialog, consultationOrderDialog, integralOrderDialog},
+  components: {userDetails, packageOrderDialog, consultationOrderDialog, integralOrderDialog, guCoinOrderDialog},
   data() {
     return {
       show:{
@@ -277,6 +320,18 @@ export default {
       orderTypeDialogVisible: false,
       selectedOrderType: 'package',
       currentOrderUser: {},
+      addGuCoinOpen: false,
+      addGuCoinForm: {
+        userId: null,
+        nickname: '',
+        currentGuCoin: 0,
+        guCoin: 1,
+        consumeAmount: null,
+        remark: ''
+      },
+      addGuCoinRules: {
+        guCoin: [{ required: true, message: "谷币数量不能为空", trigger: "blur" }]
+      },
       // 查询参数
       queryParams: {
         pageNum: 1,
@@ -456,6 +511,46 @@ export default {
           case 'integral':
             this.$refs.integralOrderDialog.init(this.currentOrderUser);
             break;
+          case 'guCoin':
+            this.$refs.guCoinOrderDialog.init(this.currentOrderUser);
+            break;
+        }
+      });
+    },
+    /** 打开添加谷币对话框 */
+    handleAddGuCoin(row) {
+      this.addGuCoinForm = {
+        userId: row.userId,
+        nickname: row.nickname,
+        currentGuCoin: row.guCoin || 0,
+        guCoin: 1,
+        consumeAmount: null,
+        remark: ''
+      };
+      this.addGuCoinOpen = true;
+      this.$nextTick(() => {
+        if (this.$refs.addGuCoinForm) {
+          this.$refs.addGuCoinForm.clearValidate();
+        }
+      });
+    },
+    /** 提交添加谷币 */
+    submitAddGuCoin() {
+      this.$refs["addGuCoinForm"].validate(valid => {
+        if (valid) {
+          var submitData = {
+            userId: this.addGuCoinForm.userId,
+            guCoin: this.addGuCoinForm.guCoin,
+            consumeAmount: this.addGuCoinForm.consumeAmount,
+            remark: this.addGuCoinForm.remark
+          };
+          addGuCoin(submitData).then(response => {
+            if (response.code === 200) {
+              this.msgSuccess("添加谷币成功");
+              this.addGuCoinOpen = false;
+              this.getList();
+            }
+          });
         }
       });
     },