|
@@ -1,133 +1,191 @@
|
|
|
<template>
|
|
|
<el-container class="group-view panel-block">
|
|
|
- <el-header class="sa-header">
|
|
|
- <!-- 简化搜索组件 -->
|
|
|
- <div class="search-container">
|
|
|
- <sa-search-simple
|
|
|
- :searchFields="searchFields"
|
|
|
- :defaultValues="defaultSearchValues"
|
|
|
- @search="(val) => getData(1, val)"
|
|
|
- @reset="getData(1)"
|
|
|
- >
|
|
|
- </sa-search-simple>
|
|
|
- </div>
|
|
|
- <div class="sa-title sa-flex sa-row-between">
|
|
|
- <div class="label sa-flex">拼团活动</div>
|
|
|
- <div>
|
|
|
- <el-button class="sa-button-refresh" icon="RefreshRight" @click="getData()"></el-button>
|
|
|
- <el-button icon="Plus" type="primary" @click="addRow">新建</el-button>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </el-header>
|
|
|
- <el-main class="sa-p-0">
|
|
|
- <div class="sa-table-wrap panel-block panel-block--bottom" v-loading="loading">
|
|
|
- <el-table
|
|
|
- height="100%"
|
|
|
- class="sa-table"
|
|
|
- :data="table.data"
|
|
|
- @selection-change="changeSelection"
|
|
|
- @sort-change="fieldFilter"
|
|
|
- @row-dblclick="editRow"
|
|
|
- row-key="id"
|
|
|
- stripe
|
|
|
- >
|
|
|
- <template #empty>
|
|
|
- <sa-empty />
|
|
|
- </template>
|
|
|
- <el-table-column type="selection" width="48" align="center"></el-table-column>
|
|
|
- <el-table-column prop="id" label="ID" min-width="100" sortable="custom">
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="活动名称" min-width="150">
|
|
|
- <template #default="scope">
|
|
|
- <span class="sa-table-line-1">
|
|
|
- {{ scope.row.title || '-' }}
|
|
|
- </span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="商品名称" min-width="150">
|
|
|
- <template #default="scope">
|
|
|
- <span class="sa-table-line-1">
|
|
|
- {{ scope.row.goods_title || '-' }}
|
|
|
- </span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="拼团人数" min-width="100">
|
|
|
- <template #default="scope"> {{ scope.row.people_num || '-' }}人 </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="拼团价格" min-width="120">
|
|
|
- <template #default="scope"> ৳{{ scope.row.group_price || '-' }} </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="状态" min-width="100">
|
|
|
- <template #default="scope">
|
|
|
- <el-tag :type="scope.row.status === 'active' ? 'success' : 'danger'">
|
|
|
- {{ scope.row.status_text || '-' }}
|
|
|
- </el-tag>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="开始时间" min-width="160">
|
|
|
- <template #default="scope">
|
|
|
- {{ scope.row.start_time || '-' }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column label="结束时间" min-width="160">
|
|
|
- <template #default="scope">
|
|
|
- {{ scope.row.end_time || '-' }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column fixed="right" label="操作" min-width="120">
|
|
|
- <template #default="scope">
|
|
|
- <el-button class="is-link" type="primary" @click="editRow(scope.row)">编辑</el-button>
|
|
|
- <el-popconfirm
|
|
|
- width="fit-content"
|
|
|
- confirm-button-text="确认"
|
|
|
- cancel-button-text="取消"
|
|
|
- title="确认删除这条记录?"
|
|
|
- @confirm="deleteApi(scope.row.id)"
|
|
|
- >
|
|
|
- <template #reference>
|
|
|
- <el-button class="is-link" type="danger"> 删除 </el-button>
|
|
|
+ <el-container class="group-index-main">
|
|
|
+ <el-container>
|
|
|
+ <el-header class="sa-header">
|
|
|
+ <!-- 简化搜索组件 -->
|
|
|
+ <div class="search-container">
|
|
|
+ <sa-search-simple
|
|
|
+ :searchFields="searchFields"
|
|
|
+ :defaultValues="defaultSearchValues"
|
|
|
+ @search="(val) => getData(1, val)"
|
|
|
+ @reset="getData(1)"
|
|
|
+ >
|
|
|
+ </sa-search-simple>
|
|
|
+ </div>
|
|
|
+ <el-tabs class="sa-tabs" v-model="currentStatus" @tab-change="handleTabChange">
|
|
|
+ <el-tab-pane :label="`全部(${statusCount.all})`" name="all"></el-tab-pane>
|
|
|
+ <el-tab-pane :label="`待开始(${statusCount.pending})`" name="pending"></el-tab-pane>
|
|
|
+ <el-tab-pane :label="`进行中(${statusCount.active})`" name="active"></el-tab-pane>
|
|
|
+ <el-tab-pane :label="`已结束(${statusCount.ended})`" name="ended"></el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ <div class="sa-title sa-flex sa-row-between">
|
|
|
+ <div class="label sa-flex">
|
|
|
+ <span class="left">拼团活动列表</span>
|
|
|
+ </div>
|
|
|
+ <div>
|
|
|
+ <el-button
|
|
|
+ class="sa-button-refresh"
|
|
|
+ icon="RefreshRight"
|
|
|
+ @click="getData()"
|
|
|
+ ></el-button>
|
|
|
+ <el-button icon="Plus" type="primary" @click="addRow">添加活动</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-header>
|
|
|
+ <el-main class="sa-p-0">
|
|
|
+ <div class="sa-table-wrap" v-loading="loading">
|
|
|
+ <el-table
|
|
|
+ height="100%"
|
|
|
+ class="sa-table"
|
|
|
+ :data="table.data"
|
|
|
+ @selection-change="changeSelection"
|
|
|
+ @sort-change="fieldFilter"
|
|
|
+ row-key="id"
|
|
|
+ stripe
|
|
|
+ >
|
|
|
+ <template #empty>
|
|
|
+ <sa-empty></sa-empty>
|
|
|
+ </template>
|
|
|
+ <el-table-column type="selection" width="48" align="center"></el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ sortable="custom"
|
|
|
+ prop="activity_no"
|
|
|
+ label="活动编号"
|
|
|
+ align="center"
|
|
|
+ ></el-table-column>
|
|
|
+ <el-table-column label="活动标题">
|
|
|
+ <template #default="scope">
|
|
|
+ <span class="sa-table-line-1">
|
|
|
+ {{ scope.row.title || '--' }}
|
|
|
+ </span>
|
|
|
</template>
|
|
|
- </el-popconfirm>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- </el-table>
|
|
|
- </div>
|
|
|
- </el-main>
|
|
|
- <sa-view-bar>
|
|
|
- <template #left>
|
|
|
- <sa-batch-handle
|
|
|
- :batchHandleTools="batchHandleTools"
|
|
|
- :selectedLeng="table.selected.length"
|
|
|
- @batchHandle="batchHandle"
|
|
|
- ></sa-batch-handle>
|
|
|
- </template>
|
|
|
- <template #right>
|
|
|
- <sa-pagination :pageData="pageData" @updateFn="getData" />
|
|
|
- </template>
|
|
|
- </sa-view-bar>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="活动状态" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag :type="statusList.color[scope.row.status]">
|
|
|
+ {{ scope.row.status_text || '--' }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column sortable="custom" prop="start_time" label="开始时间" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row.start_time || '--' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column sortable="custom" prop="end_time" label="结束时间" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row.end_time || '--' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品数量" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ <el-tag type="info" size="small"> {{ scope.row.goods_count || 0 }}个商品 </el-tag>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="预警通知" align="center">
|
|
|
+ <template #default="scope">
|
|
|
+ {{ scope.row.warning_notice || '--' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" fixed="right">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="sa-flex">
|
|
|
+ <el-button link type="primary" @click="editRow(scope.row)">编辑</el-button>
|
|
|
+ <el-button link class="sa-m-l-12" type="success" @click="setGoods(scope.row)"
|
|
|
+ >设置商品</el-button
|
|
|
+ >
|
|
|
+ <el-popconfirm
|
|
|
+ width="fit-content"
|
|
|
+ confirm-button-text="确认"
|
|
|
+ cancel-button-text="取消"
|
|
|
+ title="确认删除这条记录?"
|
|
|
+ @confirm="deleteRow(scope.row.id)"
|
|
|
+ >
|
|
|
+ <template #reference>
|
|
|
+ <el-button link class="sa-m-l-12" type="danger"> 删除 </el-button>
|
|
|
+ </template>
|
|
|
+ </el-popconfirm>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+ </el-main>
|
|
|
+ </el-container>
|
|
|
+ </el-container>
|
|
|
+ <el-footer class="group-index-footer">
|
|
|
+ <sa-view-bar>
|
|
|
+ <template #left>
|
|
|
+ <sa-batch-handle
|
|
|
+ :batchHandleTools="batchHandleTools"
|
|
|
+ :selectedLeng="table.selected.length"
|
|
|
+ @batchHandle="batchHandle"
|
|
|
+ ></sa-batch-handle>
|
|
|
+ </template>
|
|
|
+ <template #right>
|
|
|
+ <sa-pagination :pageData="pageData" @updateFn="getData" />
|
|
|
+ </template>
|
|
|
+ </sa-view-bar>
|
|
|
+ </el-footer>
|
|
|
</el-container>
|
|
|
</template>
|
|
|
+<script>
|
|
|
+ export default {
|
|
|
+ name: 'shop.admin.marketing.group',
|
|
|
+ };
|
|
|
+</script>
|
|
|
<script setup>
|
|
|
import { onMounted, reactive, ref } from 'vue';
|
|
|
- import { api } from '../marketing.service';
|
|
|
- import { ElMessageBox } from 'element-plus';
|
|
|
- import { useModal } from '@/sheep/hooks';
|
|
|
- import { usePagination } from '@/sheep/hooks';
|
|
|
+ import { ElMessage, ElMessageBox } from 'element-plus';
|
|
|
+ import { useModal, usePagination } from '@/sheep/hooks';
|
|
|
+ import { groupMockData } from '@/sheep/mock/group';
|
|
|
import groupEdit from './edit.vue';
|
|
|
+ import GoodsSelect from '@/app/shop/admin/goods/goods/select.vue';
|
|
|
+
|
|
|
const { pageData } = usePagination();
|
|
|
|
|
|
+ // 当前状态Tab
|
|
|
+ const currentStatus = ref('all');
|
|
|
+
|
|
|
+ // 状态统计
|
|
|
+ const statusCount = ref({
|
|
|
+ all: 0,
|
|
|
+ pending: 0,
|
|
|
+ active: 0,
|
|
|
+ ended: 0,
|
|
|
+ });
|
|
|
+
|
|
|
+ // 状态列表配置
|
|
|
+ const statusList = reactive({
|
|
|
+ color: {
|
|
|
+ all: '',
|
|
|
+ pending: 'warning',
|
|
|
+ active: 'success',
|
|
|
+ ended: 'info',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
// 搜索字段配置
|
|
|
const searchFields = reactive({
|
|
|
title: {
|
|
|
type: 'input',
|
|
|
- label: '活动名称',
|
|
|
- placeholder: '请输入活动名称',
|
|
|
+ label: '活动标题',
|
|
|
+ placeholder: '请输入活动标题',
|
|
|
+ width: 200,
|
|
|
+ },
|
|
|
+ activity_no: {
|
|
|
+ type: 'input',
|
|
|
+ label: '活动编号',
|
|
|
+ placeholder: '请输入活动编号',
|
|
|
width: 200,
|
|
|
},
|
|
|
});
|
|
|
+
|
|
|
// 默认搜索值
|
|
|
const defaultSearchValues = reactive({
|
|
|
title: '',
|
|
|
+ activity_no: '',
|
|
|
});
|
|
|
// 列表
|
|
|
const table = reactive({
|
|
@@ -137,26 +195,57 @@
|
|
|
selected: [],
|
|
|
});
|
|
|
const loading = ref(true);
|
|
|
- // 获取
|
|
|
+
|
|
|
+ // 获取数据
|
|
|
async function getData(page, searchParams = {}) {
|
|
|
if (page) pageData.page = page;
|
|
|
loading.value = true;
|
|
|
- const { error, data } = await api.group.list({
|
|
|
- page: pageData.page,
|
|
|
- list_rows: pageData.list_rows,
|
|
|
- order: table.order,
|
|
|
- ...searchParams,
|
|
|
- sort: table.sort,
|
|
|
- });
|
|
|
- console.log('API 响应:', error, data);
|
|
|
- if (error === 0) {
|
|
|
- table.data = data.data;
|
|
|
- pageData.page = data.current_page;
|
|
|
- pageData.list_rows = data.per_page;
|
|
|
- pageData.total = data.total;
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 使用mock数据
|
|
|
+ let mockData = [...groupMockData.list];
|
|
|
+
|
|
|
+ // 根据当前状态过滤数据
|
|
|
+ if (currentStatus.value !== 'all') {
|
|
|
+ mockData = mockData.filter((item) => item.status === currentStatus.value);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 根据搜索条件过滤
|
|
|
+ if (searchParams.title) {
|
|
|
+ mockData = mockData.filter((item) =>
|
|
|
+ item.title.toLowerCase().includes(searchParams.title.toLowerCase()),
|
|
|
+ );
|
|
|
+ }
|
|
|
+ if (searchParams.activity_no) {
|
|
|
+ mockData = mockData.filter((item) =>
|
|
|
+ item.activity_no.toLowerCase().includes(searchParams.activity_no.toLowerCase()),
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 模拟分页
|
|
|
+ const total = mockData.length;
|
|
|
+ const start = (pageData.page - 1) * pageData.list_rows;
|
|
|
+ const end = start + pageData.list_rows;
|
|
|
+ const paginatedData = mockData.slice(start, end);
|
|
|
+
|
|
|
+ // 设置数据
|
|
|
+ table.data = paginatedData;
|
|
|
+ pageData.total = total;
|
|
|
+
|
|
|
+ // 更新状态统计
|
|
|
+ statusCount.value = { ...groupMockData.statusCount };
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取数据失败:', error);
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
}
|
|
|
- loading.value = false;
|
|
|
}
|
|
|
+
|
|
|
+ // Tab切换处理
|
|
|
+ const handleTabChange = (tabName) => {
|
|
|
+ currentStatus.value = tabName;
|
|
|
+ getData(1);
|
|
|
+ };
|
|
|
// table 字段排序
|
|
|
function fieldFilter({ prop, order }) {
|
|
|
table.order = order == 'ascending' ? 'asc' : 'desc';
|
|
@@ -169,17 +258,17 @@
|
|
|
}
|
|
|
// 分页/批量操作
|
|
|
const batchHandleTools = [
|
|
|
- {
|
|
|
- type: 'delete',
|
|
|
- label: '删除',
|
|
|
- auth: 'shop.admin.marketing.group.delete',
|
|
|
- class: 'danger',
|
|
|
- },
|
|
|
+ // {
|
|
|
+ // type: 'delete',
|
|
|
+ // label: '删除',
|
|
|
+ // auth: 'shop.admin.marketing.group.delete',
|
|
|
+ // class: 'danger',
|
|
|
+ // },
|
|
|
];
|
|
|
function addRow() {
|
|
|
useModal(
|
|
|
groupEdit,
|
|
|
- { title: '新建拼团', type: 'add' },
|
|
|
+ { title: '新建拼团活动', type: 'add' },
|
|
|
{
|
|
|
confirm: () => {
|
|
|
getData();
|
|
@@ -187,11 +276,12 @@
|
|
|
},
|
|
|
);
|
|
|
}
|
|
|
+
|
|
|
function editRow(row) {
|
|
|
useModal(
|
|
|
groupEdit,
|
|
|
{
|
|
|
- title: '编辑拼团',
|
|
|
+ title: '编辑拼团活动',
|
|
|
type: 'edit',
|
|
|
id: row.id,
|
|
|
},
|
|
@@ -202,10 +292,59 @@
|
|
|
},
|
|
|
);
|
|
|
}
|
|
|
- // 删除api 单独批量可以直接调用
|
|
|
- async function deleteApi(id) {
|
|
|
- await api.group.delete(id);
|
|
|
- getData();
|
|
|
+
|
|
|
+ // 设置商品
|
|
|
+ function setGoods(row) {
|
|
|
+ useModal(
|
|
|
+ GoodsSelect,
|
|
|
+ {
|
|
|
+ title: '选择商品',
|
|
|
+ multiple: true,
|
|
|
+ ids: row.goods_ids || [], // 已选择的商品ID
|
|
|
+ },
|
|
|
+ {
|
|
|
+ confirm: (res) => {
|
|
|
+ // 更新活动的商品列表
|
|
|
+ updateActivityGoods(row.id, res.data);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新活动商品
|
|
|
+ async function updateActivityGoods(activityId, goodsList) {
|
|
|
+ try {
|
|
|
+ // 这里可以调用API更新活动商品
|
|
|
+ // 暂时使用mock数据模拟
|
|
|
+ const goodsIds = goodsList.map((item) => item.id);
|
|
|
+ const result = groupMockData.updateGoods(activityId, goodsIds, goodsList);
|
|
|
+
|
|
|
+ if (result.error === 0) {
|
|
|
+ ElMessage.success('商品设置成功');
|
|
|
+ getData(); // 刷新列表
|
|
|
+ } else {
|
|
|
+ ElMessage.error(result.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('设置商品失败:', error);
|
|
|
+ ElMessage.error('设置商品失败');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 删除单条记录
|
|
|
+ async function deleteRow(id) {
|
|
|
+ try {
|
|
|
+ const result = groupMockData.delete(id);
|
|
|
+ if (result.error === 0) {
|
|
|
+ ElMessage.success(result.msg);
|
|
|
+ getData();
|
|
|
+ } else {
|
|
|
+ ElMessage.error(result.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('删除失败:', error);
|
|
|
+ ElMessage.error('删除失败');
|
|
|
+ }
|
|
|
}
|
|
|
async function batchHandle(type) {
|
|
|
let ids = [];
|
|
@@ -219,14 +358,29 @@
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning',
|
|
|
}).then(() => {
|
|
|
- deleteApi(ids.join(','));
|
|
|
+ batchDeleteRows(ids);
|
|
|
});
|
|
|
break;
|
|
|
default:
|
|
|
- await api.group.edit(ids.join(','), {
|
|
|
- status: type,
|
|
|
- });
|
|
|
+ // 其他批量操作可以在这里添加
|
|
|
+ ElMessage.info('该功能暂未实现');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 批量删除方法
|
|
|
+ async function batchDeleteRows(ids) {
|
|
|
+ try {
|
|
|
+ const result = groupMockData.batchDelete(ids);
|
|
|
+ if (result.error === 0) {
|
|
|
+ ElMessage.success(result.msg);
|
|
|
getData();
|
|
|
+ table.selected = []; // 清空选中项
|
|
|
+ } else {
|
|
|
+ ElMessage.error(result.msg);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('批量删除失败:', error);
|
|
|
+ ElMessage.error('批量删除失败');
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -236,13 +390,41 @@
|
|
|
</script>
|
|
|
<style lang="scss" scoped>
|
|
|
.group-view {
|
|
|
+ height: 100%;
|
|
|
+
|
|
|
+ .group-index-main {
|
|
|
+ height: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .group-index-footer {
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
.el-header {
|
|
|
height: auto;
|
|
|
}
|
|
|
+
|
|
|
.el-main {
|
|
|
.sa-table-wrap {
|
|
|
height: 100%;
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ .search-container {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sa-tabs {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .sa-table-line-1 {
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ -webkit-line-clamp: 1;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ word-break: break-all;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|