Hwf il y a 2 semaines
Parent
commit
482bef6fa5

+ 14 - 4
src/components/Chart/index.vue

@@ -8,10 +8,20 @@ import { autoToolTip } from './echarts_auto_tooltip';
 import * as echarts from 'echarts';
 import { onUnmounted } from 'vue';
 
-const props = withDefaults(defineProps(), {
-  rotation: false,
-  legendChange: () => {}
-});
+const props = defineProps({
+  option: Object,
+  rotation: {
+    type: Boolean,
+    required: false,
+    default: false
+  },
+  chartClick: Function,
+  legendChange: {
+    type:Function,
+    required: false,
+    default: () => {}
+  }
+})
 const emits = defineEmits(['ready', 'click']);
 
 // 渲染容器

+ 305 - 0
src/components/DataImport/index.vue

@@ -0,0 +1,305 @@
+<template>
+  <el-dialog v-model="dialogVisible" title="批量导入" width="780px" destroy-on-close append-to-body @close="handleClose">
+    <div class="describe-content">
+      <div class="title">操作说明:</div>
+      <div class="step-container">
+        <el-steps direction="vertical">
+          <el-step status="process" title="首先点击“模板下载,下载录入模板”。" />
+          <el-step status="process" :title="'在模板空白处中填写' + stepsText + '。'" />
+          <el-step status="process" title="通过点击“导入按钮,浏览选择导入文件”,完成导入。" />
+          <el-step
+            status="process"
+            :title="'导入成功,对于系统中没有的' + stepsText + ',进行新增操作对于原本系统已存在的' + stepsText + ',进行信息合并操作。'"
+          />
+        </el-steps>
+      </div>
+    </div>
+    <template #footer>
+      <div class="dialog-footer">
+        <el-upload
+          ref="fileUploadRef"
+          multiple
+          action="#"
+          :before-upload="handleBeforeUpload"
+          :file-list="fileList"
+          :limit="limit"
+          :on-error="handleUploadError"
+          :on-exceed="handleExceed"
+          :on-success="handleUploadSuccess"
+          :show-file-list="false"
+          :headers="headers"
+          :http-request="uploadFile"
+          class="upload-file-uploader"
+        >
+          <el-button type="primary" :icon="Upload">导入</el-button>
+        </el-upload>
+        <el-button type="primary" :icon="Download" style="margin-left: 16px" @click="handleDownload">模板下载</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script lang="ts" setup name="DataImport">
+import { Upload, Download } from '@element-plus/icons-vue';
+import { download2, globalHeaders } from '@/utils/request';
+import { propTypes } from '@/utils/propTypes';
+import { v1 as uuidv1 } from 'uuid';
+import axios from 'axios';
+import { getToken } from '@/utils/auth';
+import { importDataManagerInfo } from '@/api/dataManagement/dataManagement';
+
+const baseUrl = import.meta.env.VITE_APP_BASE_API;
+const props = defineProps({
+  modelValue: Boolean,
+  // 数量限制
+  limit: propTypes.number.def(1),
+  // 大小限制(MB)
+  fileSize: propTypes.number.def(100),
+  fileType: propTypes.array.def(['xls', 'xlsx']),
+  stepsText: String,
+  url: String,
+  fileName: String,
+  dataType: String
+});
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const emits = defineEmits(['update:modelValue', 'success']);
+const dialogVisible = computed({
+  get() {
+    return props.modelValue;
+  },
+  set(newValue) {
+    emits('update:modelValue', newValue);
+  }
+});
+
+const handleClose = () => {
+  emits('update:modelValue', false);
+};
+const number = ref(0);
+const uploadList = ref<any[]>([]);
+
+const headers = ref(globalHeaders());
+
+const fileList = ref<any[]>([]);
+const fileUploadRef = ref<ElUploadInstance>();
+// 隐藏清空数据
+watch(
+  () => props.modelValue,
+  async (val) => {
+    if (!val) {
+      fileList.value = [];
+    }
+  },
+  { deep: true, immediate: true }
+);
+
+// 进入导入界面
+const handleImport = () => {};
+// 上传前校检格式和大小
+const handleBeforeUpload = (file: any) => {
+  // 校检文件类型
+  if (props.fileType.length) {
+    const fileName = file.name.split('.');
+    const fileExt = fileName[fileName.length - 1];
+    const isTypeOk = props.fileType.indexOf(fileExt) >= 0;
+    if (!isTypeOk) {
+      proxy?.$modal.msgError(`文件格式不正确, 请上传${props.fileType.join('/')}格式文件!`);
+      return false;
+    }
+  }
+  // 校检文件大小
+  if (props.fileSize) {
+    const isLt = file.size / 1024 / 1024 < props.fileSize;
+    if (!isLt) {
+      proxy?.$modal.msgError(`上传文件大小不能超过 ${props.fileSize} MB!`);
+      return false;
+    }
+  }
+  proxy?.$modal.loading('正在上传文件,请稍候...');
+  number.value++;
+  return true;
+};
+
+// 文件个数超出
+const handleExceed = () => {
+  proxy?.$modal.msgError(`上传文件数量不能超过 ${props.limit} 个!`);
+};
+
+// 上传失败
+const handleUploadError = () => {
+  proxy?.$modal.msgError('上传文件失败');
+};
+
+const uploadFile = async ({ data, file }) => {
+  // data是上传时附带的额外参数,file是文件
+  let url = baseUrl + '/file/upload/uploadfile'; //上传文件接口
+  try {
+    // 如果文件大于等于5MB,分片上传
+    data.file = file;
+    const res = await uploadByPieces(url, data);
+    // 分片上传后操作
+    return res;
+  } catch (e) {
+    return e;
+  }
+};
+//分片上传
+const uploadByPieces = async (url, { fileName, file }) => {
+  // 上传过程中用到的变量
+  const chunkSize = 5 * 1024 * 1024; // 5MB一片
+  const chunkCount = Math.ceil(file.size / chunkSize); // 总片数
+  // 获取当前chunk数据
+  const identifier = uuidv1();
+  const getChunkInfo = (file, index) => {
+    let start = index * chunkSize;
+    let end = Math.min(file.size, start + chunkSize);
+    let chunknumber = file.slice(start, end);
+    const fileName = file.name;
+    return { chunknumber, fileName };
+  };
+  // 分片上传接口
+  const uploadChunk = (data, params) => {
+    return new Promise((resolve, reject) => {
+      axios({
+        url,
+        method: 'post',
+        data,
+        params,
+        headers: {
+          'Content-Type': 'multipart/form-data',
+          'Authorization': 'Bearer ' + getToken()
+        }
+      })
+        .then((res) => {
+          return resolve(res.data);
+        })
+        .catch((err) => {
+          return reject(err);
+        });
+    });
+  };
+  // 针对单个文件进行chunk上传
+  const readChunk = (index) => {
+    const { chunknumber } = getChunkInfo(file, index);
+    let fetchForm = new FormData();
+    fetchForm.append('file', chunknumber);
+    return uploadChunk(fetchForm, {
+      chunknumber: index,
+      identifier: identifier
+    });
+  };
+  // 针对每个文件进行chunk处理
+  const promiseList = [];
+  try {
+    for (let index = 0; index < chunkCount; ++index) {
+      promiseList.push(readChunk(index));
+    }
+    await Promise.all(promiseList);
+    // 文件分片上传完成后,调用合并接口
+
+    const res = await mergeChunks(identifier, file.name);
+    res.originalName = file.name;
+    const res2 = await importDataManagerInfo(props.dataType, res.filename);
+    // const res3 = await customUpload(identifier, file.name);
+    return res;
+  } catch (e) {
+    return e;
+  }
+};
+const mergeChunks = async (identifier: string, filename: string) => {
+  try {
+    const response = await axios.post(baseUrl + '/file/upload/mergefile', null, {
+      params: {
+        identifier: identifier,
+        filename: filename,
+        chunkstar: 0 // 假设所有分片的开始序号为0
+      },
+      headers: {
+        'Authorization': 'Bearer ' + getToken()
+      }
+    });
+
+    return response.data;
+  } catch (error) {
+    throw new Error('合并请求失败');
+  }
+};
+// 上传成功回调
+const handleUploadSuccess = (res: any, file: UploadFile) => {
+  if (res && res.code === 200) {
+    uploadList.value.push({
+      name: res.originalName,
+      url: res.filename
+    });
+    uploadedSuccessfully();
+    proxy.$modal.msgSuccess('导入成功');
+    emits('update:modelValue', false);
+    emits('success');
+  } else {
+    number.value--;
+    proxy?.$modal.closeLoading();
+    proxy?.$modal.msgError(res.msg);
+    fileUploadRef.value?.handleRemove(file);
+    uploadedSuccessfully();
+    proxy.$modal.msgError('导入失败');
+  }
+};
+
+// 上传结束处理
+const uploadedSuccessfully = () => {
+  if (number.value > 0 && uploadList.value.length === number.value) {
+    fileList.value = fileList.value.filter((f) => f.url !== undefined).concat(uploadList.value);
+    uploadList.value = [];
+    number.value = 0;
+    proxy?.$modal.closeLoading();
+  }
+};
+
+// 下载模板
+const handleDownload = () => {
+  if (!props.url) return false;
+  let downLoadUrl = baseUrl + props.url;
+  download2(downLoadUrl, props.fileName);
+};
+</script>
+
+<style lang="scss" scoped>
+.title {
+  font-size: 18px;
+  line-height: 36px;
+  margin-bottom: 10px;
+  color: rgba(0, 0, 0, 0.85);
+}
+.step-container {
+  width: 700px;
+  height: 180px;
+  :deep(.el-step) {
+    .el-step__icon.is-text {
+      border-color: #a8abb2;
+      color: rgba(0, 0, 0, 0.85);
+    }
+    .el-step__title.is-process {
+      font-size: 16px;
+      color: rgba(0, 0, 0, 0.85);
+      font-weight: normal;
+    }
+  }
+}
+.upload-file-list .el-upload-list__item {
+  line-height: 22px;
+  margin-bottom: 10px;
+  position: relative;
+  padding: 3px 5px;
+}
+
+.upload-file-list .ele-upload-list__item-content {
+  display: flex;
+  align-items: center;
+  color: inherit;
+}
+
+.ele-upload-list__item-content-action .el-link {
+  margin-right: 10px;
+}
+</style>

+ 29 - 41
src/views/orderList/index.vue

@@ -3,47 +3,35 @@
     <div v-show="!eventDetailsState.show">
       <FilterButton :filter-items="filterItems" :active-value="queryParams.activeFilter" @filter-change="handleFilterChange" />
       <transition name="fade">
-        <div>
-          <el-form ref="queryFormRef" :model="queryParams">
-            <el-row :gutter="20">
-              <el-col :span="7">
-                <el-form-item label="输入搜索:" prop="name">
-                  <el-input v-model="queryParams.name" placeholder="请输入订单编号" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="7">
-                <el-form-item label="收货人:" prop="consignee">
-                  <el-input v-model="queryParams.consignee" placeholder="请输入用户账号" clearable @keyup.enter="handleQuery" />
-                </el-form-item>
-              </el-col>
-              <el-col :span="7">
-                <el-form-item label="商品类型:" prop="submissionTime">
-                  <el-select v-model="queryParams.serviceStatus" clearable>
-                    <el-option v-for="item in service_class" :key="item.value" :label="item.label" :value="item.value"></el-option>
-                  </el-select>
-                </el-form-item>
-              </el-col>
-              <el-col :span="7">
-                <el-form-item label="提交时间:">
-                  <el-date-picker
-                    v-model="value1"
-                    type="datetimerange"
-                    start-placeholder="开始时间"
-                    end-placeholder="结束时间"
-                    format="YYYY-MM-DD HH:mm:ss"
-                    value-format="YYYY-MM-DD HH:mm:ss"
-                    date-format="YYYY/MM/DD"
-                    time-format="hh:mm:ss"
-                  />
-                </el-form-item>
-              </el-col>
-              <el-col :span="1.5">
-                <el-button type="primary" @click="handleQuery">查询</el-button>
-                <el-button @click="resetQuery">重置</el-button>
-              </el-col>
-            </el-row>
-          </el-form>
-        </div>
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="70px">
+          <el-form-item label="输入搜索:" prop="name">
+            <el-input v-model="queryParams.name" placeholder="请输入订单编号" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="收货人:" prop="consignee">
+            <el-input v-model="queryParams.consignee" placeholder="请输入用户账号" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="商品类型:" prop="submissionTime">
+            <el-select v-model="queryParams.serviceStatus" clearable>
+              <el-option v-for="item in service_class" :key="item.value" :label="item.label" :value="item.value"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="提交时间:">
+            <el-date-picker
+              v-model="value1"
+              type="datetimerange"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              date-format="YYYY/MM/DD"
+              time-format="hh:mm:ss"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleQuery">搜索</el-button>
+            <el-button @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
       </transition>
       <el-row :gutter="10" class="mb8">
         <el-col :span="1.5">

+ 270 - 0
src/views/orderList/refundHistory.vue

@@ -0,0 +1,270 @@
+<template>
+  <div class="app-container">
+    <div v-show="!eventDetailsState.show">
+      <FilterButton :filter-items="filterItems" :active-value="queryParams.activeFilter" @filter-change="handleFilterChange" />
+      <transition name="fade">
+        <el-form ref="queryFormRef" :model="queryParams" :inline="true" label-width="70px">
+          <el-form-item label="输入搜索:" prop="name">
+            <el-input v-model="queryParams.name" placeholder="请输入订单编号" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="收货人:" prop="consignee">
+            <el-input v-model="queryParams.consignee" placeholder="请输入用户账号" clearable @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item label="商品类型:" prop="submissionTime">
+            <el-select v-model="queryParams.serviceStatus" clearable>
+              <el-option v-for="item in service_class" :key="item.value" :label="item.label" :value="item.value"></el-option>
+            </el-select>
+          </el-form-item>
+          <el-form-item label="提交时间:">
+            <el-date-picker
+              v-model="value1"
+              type="datetimerange"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              date-format="YYYY/MM/DD"
+              time-format="hh:mm:ss"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="handleQuery">搜索</el-button>
+            <el-button @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </transition>
+      <el-row :gutter="10" class="mb8">
+        <el-col :span="1.5">
+          <el-button type="warning" plain icon="Download" @click="handleExport">导出</el-button>
+        </el-col>
+      </el-row>
+      <!-- 表格组件 -->
+      <el-table ref="multipleTable" v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
+        <el-table-column label="订单编号" align="center" prop="id" />
+        <el-table-column label="提交时间" align="center" prop="submissionTime" />
+        <el-table-column label="用户账号" align="center" prop="userAccount" />
+        <el-table-column label="订单金额" align="center" prop="orderAmount" />
+        <el-table-column label="支付方式" align="center" prop="paymentMethod" />
+        <el-table-column label="商品分类" align="center" prop="category" />
+        <el-table-column label="订单状态" align="center" prop="orderStatus" />
+        <el-table-column label="接口状态" align="center" prop="status" />
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <!-- 待签署和待付款状态显示取消订单 -->
+            <el-text v-if="['待签署', '待付款'].includes(scope.row.orderStatus)" class="common-btn-text-danger" @click="cancelOrder(scope.row.id)">
+              取消订单
+            </el-text>
+
+            <!-- 已完成状态显示查看详情 -->
+            <el-text v-if="scope.row.orderStatus === '已完成'" class="common-btn-text-primary" @click="viewDetails(scope.row)"> 查看详情 </el-text>
+
+            <!-- 已退款和已取消状态不显示操作按钮 -->
+            <span v-if="['已退款', '已取消'].includes(scope.row.orderStatus)">-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination v-show="total > 0" v-model:page="queryParams.page" v-model:limit="queryParams.page_size" :total="total" @pagination="getList" />
+    </div>
+    <order-details v-if="eventDetailsState.show" :id="eventDetailsState.id" @close="handleEventDetailsClose" />
+  </div>
+</template>
+
+<script setup name="refundHistory">
+import OrderDetails from '@/views/orderList/orderDetails.vue';
+import { addDateRange2 } from '@/utils/ruoyi.js';
+
+const { proxy } = getCurrentInstance();
+const { refund_apply_status } = toRefs(proxy?.useDict('refund_apply_status'));
+const queryFormRef = ref();
+const value1 = ref([]);
+// 搜索条件
+const queryParams = reactive({
+  name: '',
+  serviceClass: '',
+  serviceStatus: '',
+  page: 1,
+  page_size: 10,
+  activeFilter: 'all'
+});
+const filterItems = [
+  { label: '全部申请', value: 'all', sum: 3 },
+  { label: '待处理', value: '0', sum: 1 },
+  { label: '已处理', value: '1', sum: 1 },
+  { label: '已拒绝', value: '2', sum: 1 }
+];
+// 表格数据
+const originalData = [
+  {
+    id: 'DD20230515001',
+    submissionTime: '2023-05-15 09:30:45',
+    userAccount: 'user_zhangsan',
+    orderAmount: '¥258.00',
+    paymentMethod: '微信支付',
+    category: '电子产品',
+    orderStatus: '待签署',
+    status: '正常'
+  },
+  {
+    id: 'DD20230515002',
+    submissionTime: '2023-05-15 10:15:22',
+    userAccount: 'user_lisi',
+    orderAmount: '¥599.00',
+    paymentMethod: '支付宝',
+    category: '家居用品',
+    orderStatus: '待付款',
+    status: '正常'
+  },
+  {
+    id: 'DD20230515003',
+    submissionTime: '2023-05-15 13:45:18',
+    userAccount: 'user_wangwu',
+    orderAmount: '¥1299.00',
+    paymentMethod: '银联云闪付',
+    category: '家用电器',
+    orderStatus: '已取消',
+    status: '正常'
+  },
+  {
+    id: 'DD20230515004',
+    submissionTime: '2023-05-15 15:20:33',
+    userAccount: 'user_zhaoliu',
+    orderAmount: '¥89.00',
+    paymentMethod: '微信支付',
+    category: '食品饮料',
+    orderStatus: '已完成',
+    status: '已关闭'
+  },
+  {
+    id: 'DD20230515005',
+    submissionTime: '2023-05-15 18:05:07',
+    userAccount: 'user_qianqi',
+    orderAmount: '¥1999.00',
+    paymentMethod: '支付宝',
+    category: '数码产品',
+    orderStatus: '已退款',
+    status: '异常'
+  }
+];
+// 加载中
+const loading = ref(false);
+// 总数
+const total = ref(0);
+// 表格选中的所有id
+const ids = ref([]);
+// 表格选中一个
+const single = ref(true);
+// 表格选中多个
+const multiple = ref(true);
+// 选中行的数据
+const selectedRow = ref(null);
+
+// 获取数据
+const getList = () => {
+  loading.value = true;
+  const query = addDateRange2(queryParams, value1.value, 'startTime', 'endTime');
+  setTimeout(() => {
+    loading.value = false;
+  }, 500);
+};
+
+// 点击查询
+const handleQuery = () => {
+  queryParams.page = 1;
+  getList();
+};
+
+// 重置查询条件
+const resetQuery = () => {
+  if (queryFormRef.value) {
+    queryFormRef.value.resetFields(); // 重置表单并清除校验状态
+  }
+  // 重置日期选择器
+  value1.value = null;
+
+  queryParams.page = 1;
+  getList();
+};
+
+const cancelOrder = (id) => {
+  proxy?.$modal.confirm('确定要取消订单吗?').then(() => {
+
+  });
+};
+
+// 多选框选中数据
+const handleSelectionChange = (selection) => {
+  ids.value = selection.map((item) => item.reportId);
+  selectedRow.value = selection.length === 1 ? selection[0] : null;
+  single.value = selection.length != 1;
+  multiple.value = !selection.length;
+};
+
+const dataList = computed(() => {
+  if (queryParams.activeFilter === 'all') {
+    return originalData;
+  }
+  const statusMap = {
+    'pending_sign': '待签署',
+    'pending_payment': '待付款',
+    'cancelled': '已取消',
+    'completed': '已完成',
+    'refunded': '已退款'
+  };
+  return originalData.filter((item) => item.orderStatus === statusMap[queryParams.activeFilter]);
+});
+
+const handleFilterChange = (value) => {
+  queryParams.activeFilter = value;
+  // 这里可以添加根据筛选条件重新获取数据的逻辑
+  getList();
+};
+
+const handleExport = () => {};
+let eventDetailsState = reactive({
+  show: false,
+  id: ''
+});
+const viewDetails = (row) => {
+  if (row) {
+    eventDetailsState.id = row.id;
+    eventDetailsState.show = true;
+  }
+};
+const handleEventDetailsClose = () => {
+  eventDetailsState.show = false;
+};
+
+getList();
+</script>
+
+<style lang="scss" scoped>
+.status-filter {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-bottom: 20px;
+}
+
+.filter-btn {
+  padding: 6px 16px;
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  background-color: #fff;
+  color: #606266;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.filter-btn:hover {
+  color: #409eff;
+  border-color: #c6e2ff;
+  background-color: #ecf5ff;
+}
+
+.filter-btn.active {
+  color: #409eff;
+  border-color: #409eff;
+  background-color: #ecf5ff;
+}
+</style>