This commit is contained in:
gtz
2023-07-26 09:54:50 +08:00
commit 0fca3e1ec4
677 changed files with 80083 additions and 0 deletions

View File

@@ -0,0 +1,261 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="标题" prop="title">
<el-input v-model="queryParams.title" placeholder="请输入标题" clearable @keyup.enter.native="handleQuery"/>
</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 this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['market:banner:create']">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="标题" align="center" prop="title"/>
<el-table-column label="缩略图" align="center" prop="picUrl">
<template v-slot="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="缩略图片" class="img-height"/>
</template>
</el-table-column>
<el-table-column label="跳转链接" align="center" prop="url"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="描述" align="center" prop="memo"/>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['market:banner:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['market:banner:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="标题" prop="title">
<el-input v-model="form.title" placeholder="请输入标题"/>
</el-form-item>
<el-form-item label="缩略图" prop="picUrl">
<imageUpload v-model="form.picUrl" :limit="1"/>
</el-form-item>
<el-form-item label="跳转链接" prop="url">
<el-input v-model="form.url" placeholder="请输入跳转链接"/>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model="form.sort" placeholder="请输入排序"/>
</el-form-item>
<el-form-item label="描述" prop="memo">
<el-input v-model="form.memo" type="textarea" placeholder="请输入描述"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createBanner,
deleteBanner,
getBanner,
getBannerPage,
updateBanner
} from "@/api/mall/market/banner";
import ImageUpload from '@/components/ImageUpload';
export default {
name: "Banner",
components: {
ImageUpload
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 品牌列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
title: null,
status: null,
createTime: []
},
// 商品分类树选项
categoryOptions: [],
// 表单参数
form: {},
// 表单校验
rules: {
title: [{required: true, message: "标题不能不空", trigger: "blur"}],
picUrl: [{required: true, message: "图片地址不能为空", trigger: "blur"}],
url: [{required: true, message: "跳转地址不能为空", trigger: "blur"}],
sort: [{required: true, message: "排序不能为空", trigger: "blur"}],
status: [{required: true, message: "状态不能为空", trigger: "change"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getBannerPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
title: undefined,
link: undefined,
imgUrl: undefined,
sort: undefined,
memo: undefined,
status: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加Banner";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getBanner(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改Banner";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateBanner(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createBanner(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除Banner编号为"' + id + '"的数据项?').then(function () {
return deleteBanner(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
}
}
};
</script>
<style scoped lang="scss">
//
.img-height {
height: 150px;
}
</style>

View File

@@ -0,0 +1,247 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入品牌名称" clearable @keyup.enter.native="handleQuery"/>
</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 this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:brand:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="品牌编号" align="center" prop="id"/>
<el-table-column label="品牌名称" align="center" prop="name"/>
<el-table-column label="品牌图片" align="center" prop="picUrl">
<template v-slot="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px;" />
</template>
</el-table-column>
<el-table-column label="品牌排序" align="center" prop="sort"/>
<el-table-column label="品牌描述" align="center" prop="description"/>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:brand:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:brand:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="form.name" placeholder="请输入品牌名称"/>
</el-form-item>
<el-form-item label="品牌图片" prop="picUrl">
<imageUpload v-model="form.picUrl" :limit="1"/>
</el-form-item>
<el-form-item label="品牌排序" prop="sort">
<el-input v-model="form.sort" placeholder="请输入品牌排序"/>
</el-form-item>
<el-form-item label="品牌描述" prop="description">
<el-input v-model="form.description" type="textarea" placeholder="请输入内容"/>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createBrand,
deleteBrand,
getBrand,
getBrandPage,
updateBrand
} from "@/api/mall/product/brand";
import ImageUpload from '@/components/ImageUpload';
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "ProductBrand",
components: {
ImageUpload
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 品牌列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
categoryId: null,
name: null,
status: null,
createTime: []
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{required: true, message: "品牌名称不能为空", trigger: "blur"}],
picUrl: [{required: true, message: "品牌图片不能为空", trigger: "blur"}],
sort: [{required: true, message: "品牌排序不能为空", trigger: "blur"}],
status: [{required: true, message: "状态不能为空", trigger: "change"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getBrandPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
picUrl: undefined,
sort: 0,
description: undefined,
status: CommonStatusEnum.ENABLE,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加品牌";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getBrand(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改品牌";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateBrand(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createBrand(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除品牌编号为"' + id + '"的数据项?').then(function () {
return deleteBrand(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
}
}
};
</script>

View File

@@ -0,0 +1,270 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="分类名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入分类名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:category:create']">新增
</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="el-icon-sort" size="mini" @click="toggleExpandAll">展开/折叠</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-if="refreshTable" v-loading="loading" :data="list" row-key="id" :default-expand-all="isExpandAll"
:tree-props="{children: 'children', hasChildren: 'hasChildren'}">
<el-table-column label="分类名称" prop="name"/>
<el-table-column label="分类图片" align="center" prop="picUrl">
<template v-slot="scope">
<img v-if="scope.row.picUrl" :src="scope.row.picUrl" alt="分类图片" style="height: 100px"/>
</template>
</el-table-column>
<el-table-column label="分类排序" align="center" prop="sort"/>
<el-table-column label="开启状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:category:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:category:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="上级分类" prop="parentId">
<Treeselect v-model="form.parentId" :options="parentCategoryOptions" :normalizer="normalizer" :show-count="true"
:defaultExpandLevel="1" placeholder="上级分类"/>
</el-form-item>
<el-form-item label="分类名称" prop="name">
<el-input v-model="form.name" placeholder="请输入分类名称"/>
</el-form-item>
<el-form-item label="分类图片" prop="picUrl">
<ImageUpload v-model="form.picUrl" :limit="1" :is-show-tip="false" />
<div v-if="form.parentId === 0" style="font-size: 10px">推荐 200x100 图片分辨率</div>
<div v-else style="font-size: 10px">推荐 100x100 图片分辨率</div>
</el-form-item>
<el-form-item label="分类排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" />
</el-form-item>
<el-form-item label="开启状态" prop="status">
<el-radio-group v-model="form.status">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="parseInt(dict.value)">{{ dict.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="分类描述">
<el-input v-model="form.description" type="textarea" placeholder="请输入分类描述" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createProductCategory,
deleteProductCategory,
getProductCategory,
getProductCategoryList,
updateProductCategory
} from "@/api/mall/product/category";
import Editor from '@/components/Editor';
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
import ImageUpload from '@/components/ImageUpload';
import {CommonStatusEnum} from "@/utils/constants";
export default {
name: "ProductCategory",
components: {
Editor, Treeselect, ImageUpload
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 商品分类列表
list: [],
// 商品分类树选项
parentCategoryOptions: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 是否展开,默认全部折叠
isExpandAll: false,
// 重新渲染表格状态
refreshTable: true,
// 查询参数
queryParams: {
name: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
parentId: [{required: true, message: "请选择上级分类", trigger: "blur"}],
name: [{required: true, message: "分类名称不能为空", trigger: "blur"}],
picUrl: [{required: true, message: "分类图片不能为空", trigger: "blur"}],
sort: [{required: true, message: "分类排序不能为空", trigger: "blur"}],
status: [{required: true, message: "开启状态不能为空", trigger: "blur"}],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
// 执行查询
getProductCategoryList(params).then(response => {
this.list = this.handleTree(response.data, "id", "parentId");
this.loading = false;
// 属性下拉框
this.parentCategoryOptions = [];
const menu = {id: 0, name: '顶级分类', children: []};
menu.children = this.handleTree(response.data, "id", "parentId");
this.parentCategoryOptions.push(menu);
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
parentId: undefined,
name: undefined,
pirUrl: undefined,
sort: 0,
description: undefined,
status: CommonStatusEnum.ENABLE,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 展开/折叠操作 */
toggleExpandAll() {
this.refreshTable = false;
this.isExpandAll = !this.isExpandAll;
this.$nextTick(() => {
this.refreshTable = true;
});
},
/** 转换菜单数据结构 */
normalizer(node) {
if (node.children && !node.children.length) {
delete node.children;
}
return {
id: node.id,
label: node.name,
children: node.children
};
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加商品分类";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getProductCategory(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改商品分类";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateProductCategory(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createProductCategory(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除商品分类编号为"' + id + '"的数据项?').then(function () {
return deleteProductCategory(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
}
}
};
</script>

View File

@@ -0,0 +1,207 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:property:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="编号" align="center" prop="id" />
<el-table-column label="名称" align="center" :show-overflow-tooltip="true">
<template v-slot="scope">
<router-link :to="'/property/value/' + scope.row.id" class="link-type">
<span>{{ scope.row.name }}</span>
</router-link>
</template>
</el-table-column>
<el-table-column label="备注" align="center" prop="remark" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:property:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:property:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createProperty, updateProperty, deleteProperty, getProperty, getPropertyPage } from "@/api/mall/product/property";
export default {
name: "ProductProperty",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 属性项列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
createTime: []
},
// 表单参数
form: {
name:'',
remark:"",
id: null,
},
// 表单校验
rules: {
name: [
{ required: true, message: "名称不能为空", trigger: "blur" }
]
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getPropertyPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
name:'',
remark:"",
id: null,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加属性项";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getProperty(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改属性项";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateProperty(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createProperty(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除名称为"' + row.name + '"的数据项?').then(function() {
return deleteProperty(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
}
};
</script>

View File

@@ -0,0 +1,240 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="属性项" prop="propertyId">
<el-select v-model="queryParams.propertyId">
<el-option v-for="item in propertyOptions" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['system:dict:create']">新增
</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<el-table v-loading="loading" :data="dataList">
<el-table-column label="编号" align="center" prop="id"/>
<el-table-column label="名称" align="center" prop="name"/>
<el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['system:dict:update']">修改
</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['system:dict:delete']">删除
</el-button>
</template>
</el-table-column>
</el-table>
<pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="90px">
<el-form-item label="属性项">
<el-input v-model="form.propertyId" :disabled="true"/>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称"/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createPropertyValue,
deletePropertyValue,
getProperty,
getPropertyList,
getPropertyValue,
getPropertyValuePage,
updatePropertyValue
} from '@/api/mall/product/property'
export default {
name: "ProductPropertyValue",
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 字典表格数据
dataList: [],
// 默认字典类型
defaultPropertyId: "",
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 类型数据字典
propertyOptions: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
propertyId: undefined,
name: undefined,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [
{required: true, message: "名称不能为空", trigger: "blur"}
]
},
};
},
created() {
const propertyId = this.$route.params && this.$route.params.propertyId;
this.getProperty(propertyId);
this.getPropertyList();
},
methods: {
/** 查询字典类型详细 */
getProperty(propertyId) {
getProperty(propertyId).then(response => {
this.queryParams.propertyId = response.data.id;
this.defaultPropertyId = response.data.id;
this.getList();
});
},
/** 查询字典类型列表 */
getPropertyList() {
getPropertyList().then(response => {
this.propertyOptions = response.data
});
},
/** 查询字典数据列表 */
getList() {
this.loading = true;
getPropertyValuePage(this.queryParams).then(response => {
this.dataList = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
// 取消按钮
cancel() {
this.open = false;
this.reset();
},
// 表单重置
reset() {
this.form = {
id: undefined,
propertyId: undefined,
name: undefined,
remark: undefined
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.queryParams.propertyId = this.defaultPropertyId;
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加属性值";
this.form.propertyId = this.queryParams.propertyId;
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getPropertyValue(id).then(response => {
this.form = response.data;
this.open = true;
this.title = "修改属性值";
});
},
/** 提交按钮 */
submitForm: function () {
this.$refs["form"].validate(valid => {
if (valid) {
if (this.form.id !== undefined) {
updatePropertyValue(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
} else {
createPropertyValue(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
}
}
});
},
/** 删除按钮操作 */
handleDelete(row) {
const ids = row.id;
this.$modal.confirm('是否确认删除字典编码为"' + ids + '"的数据项?').then(function () {
return deletePropertyValue(ids);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
},
/** 导出按钮操作 */
handleExport() {
const queryParams = this.queryParams;
this.$modal.confirm('是否确认导出所有数据项?').then(() => {
this.exportLoading = true;
return exportData(queryParams);
}).then(response => {
this.$download.excel(response, '字典数据.xls');
this.exportLoading = false;
}).catch(() => {
});
}
}
};
</script>

View File

@@ -0,0 +1,392 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入商品名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商品编码" prop="code">
<el-input v-model="queryParams.code" placeholder="请输入商品编码" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="商品分类" prop="categoryIds">
<el-cascader v-model="queryParams.categoryIds" placeholder="请输入商品分类"
:options="categoryList" :props="propName" clearable ref="category"/>
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<el-select v-model="queryParams.brandId" placeholder="请输入商品品牌" clearable @keyup.enter.native="handleQuery">
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<!-- TODO 待实现商品类型 -->
<!-- TODO 待实现商品标签 -->
<!-- TODO 待实现营销活动 -->
<!-- TODO 前端优化商品销量商品价格排的整齐一点 -->
<el-form-item label="商品销量">
<el-col :span="7" style="padding-left:0">
<el-form-item prop="salesCountMin">
<el-input v-model="queryParams.salesCountMin" placeholder="最低销量" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
<el-col :span="1">-</el-col>
<el-col :span="7" style="padding-left:0">
<el-form-item prop="salesCountMax">
<el-input v-model="queryParams.salesCountMax" placeholder="最高销量" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item label="商品价格" prop="code">
<el-col :span="7" style="padding-left:0">
<el-form-item prop="marketPriceMin">
<el-input v-model="queryParams.marketPriceMin" placeholder="最低价格" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
<el-col :span="1">-</el-col>
<el-col :span="7" style="padding-left:0">
<el-form-item prop="marketPriceMax">
<el-input v-model="queryParams.marketPriceMax" placeholder="最高价格" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['product:spu:create']">添加商品</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"/>
</el-row>
<el-tabs v-model="activeTabs" type="card" @tab-click="handleClick">
<!-- 全部 -->
<el-tab-pane label="全部" name="all">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height" />
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
<!-- TODO 前端优化可以有个 + 点击后展示每个 sku -->
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 销售中 -->
<el-tab-pane label="销售中" name="on">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 仓库中 -->
<el-tab-pane label="仓库中" name="off">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
<!-- 预警中 -->
<el-tab-pane label="预警中" name="remind">
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="商品信息" align="center" width="260">
<template v-slot="scope">
<div class="product-info">
<img v-if="scope.row.picUrls" :src="scope.row.picUrls[0]" alt="分类图片" class="img-height"/>
<div class="message">{{ scope.row.name }}</div>
</div>
</template>
</el-table-column>
<el-table-column label="价格" align="center" prop="marketPrice" :formatter="formatPrice"/>
<el-table-column label="库存" align="center" prop="totalStock"/>
<el-table-column label="销量" align="center" prop="salesCount"/>
<el-table-column label="排序" align="center" prop="sort"/>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PRODUCT_SPU_STATUS" :value="scope.row.status"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['product:spu:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['product:spu:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import {deleteSpu, getSpuPage,} from "@/api/mall/product/spu";
import {getProductCategoryList} from "@/api/mall/product/category";
import {getBrandList} from "@/api/mall/product/brand";
import {ProductSpuStatusEnum} from "@/utils/constants";
export default {
name: "ProductSpu",
data() {
return {
activeTabs: "all",
propName: {
checkStrictly: true,
label: "name",
value: "id",
},
brandList: [],
categoryList: [],
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 商品spu列表
list: [],
dateRangeCreateTime: [],
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
code: null,
categoryIds: [],
categoryId: null,
brandId: null,
status: null,
salesCountMin: null,
salesCountMax: null,
marketPriceMin: null,
marketPriceMax: null,
},
};
},
created() {
this.getList();
this.getListCategory()
this.getListBrand()
},
methods: {
/** 查询分类列表 */
getListCategory() {
// 执行查询
getProductCategoryList().then((response) => {
this.categoryList = this.handleTree(response.data, "id", "parentId");
});
},
/** 查询品牌列表 */
getListBrand() {
// 执行查询
getBrandList().then((response) => {
this.brandList = response.data;
});
},
/** 查询列表 */
getList() {
this.loading = true;
// 处理查询参数
let params = {...this.queryParams};
params.marketPriceMin = this.queryParams.marketPriceMin === null ? null : params.marketPriceMin * 100;
params.marketPriceMax = this.queryParams.marketPriceMax === null ? null : params.marketPriceMax * 100;
params.categoryId = this.queryParams.categoryIds[this.queryParams.categoryIds.length - 1];
this.addBeginAndEndTime(params, this.dateRangeCreateTime, "createTime");
// 执行查询
getSpuPage(params).then((response) => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.dateRangeCreateTime = [];
this.$refs.category.$refs.panel.checkedValue = [];//也可以是指定的值,默认返回值是数组,也可以返回单个值
this.$refs.category.$refs.panel.activePath = [];
this.$refs.category.$refs.panel.syncActivePath();
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.$router.push({ name: 'ProductSpuCreate'})
},
/** 修改按钮操作 */
handleUpdate(row) {
this.$router.push({ name: 'ProductSpuUpdate', params: { spuId: row.id }})
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal
.confirm('是否确认删除商品spu编号为"' + id + '"的数据项?')
.then(function () {
return deleteSpu(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {
});
},
formatPrice(row, column, cellValue) {
return '¥' + this.divide(cellValue, 100);
},
// 选中 tab
handleClick(val) {
if (val.name === "all") {
this.queryParams.status = undefined;
this.queryParams.alarmStock = undefined;
} else if (val.name === "on") {
this.queryParams.status = ProductSpuStatusEnum.ENABLE.status;
this.queryParams.alarmStock = undefined;
} else if (val.name === "off") {
this.queryParams.status = ProductSpuStatusEnum.DISABLE.status;
this.queryParams.alarmStock = undefined;
} else if (val.name === "remind") {
this.queryParams.status = undefined;
this.queryParams.alarmStock = true;
}
this.getList();
}
},
};
</script>
<style lang="scss">
.app-container {
.el-tag + .el-tag {
margin-left: 10px;
}
.product-info {
display: flex;
.img-height {
height: 50px;
width: 50px;
}
.message {
margin-left: 10px;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
word-break: break-all;
-webkit-box-orient: vertical;
white-space: normal;
overflow: hidden;
height: 50px;
line-height: 25px;
}
}
}
</style>

View File

@@ -0,0 +1,580 @@
<template>
<div class="container">
<!-- TODO 样式优化表单宽度表单项对齐hr 粗细 -->
<el-tabs v-model="activeName" class="tabs">
<!-- 基础设置 -->
<!-- TODO @luowenfeng基础设置分成基础信息配送信息 -->
<el-tab-pane label="基础设置" name="basic">
<el-form ref="basic" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form-item label="商品名称" prop="name">
<el-input v-model="baseForm.name" placeholder="请输入商品名称" />
</el-form-item>
<el-form-item label="促销语">
<el-input type="textarea" v-model="baseForm.sellPoint" placeholder="请输入促销语"/>
</el-form-item>
<el-form-item label="商品主图" prop="picUrls">
<ImageUpload v-model="baseForm.picUrls" :value="baseForm.picUrls" :limit="10" class="mall-image"/>
</el-form-item>
<el-form-item label="商品视频" prop="videoUrl">
<VideoUpload v-model="baseForm.videoUrl" :value="baseForm.videoUrl"/>
</el-form-item>
<el-form-item label="商品品牌" prop="brandId">
<el-select v-model="baseForm.brandId" placeholder="请选择商品品牌">
<el-option v-for="item in brandList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</el-form-item>
<el-form-item label="商品分类" prop="categoryIds">
<el-cascader v-model="baseForm.categoryIds" placeholder="商品分类" style="width: 100%"
:options="categoryList" :props="propName" clearable/>
</el-form-item>
<el-form-item label="是否上架" prop="status">
<el-radio-group v-model="baseForm.status">
<el-radio :label="1">立即上架</el-radio>
<el-radio :label="0">放入仓库</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 价格库存 -->
<!-- TODO @luowenfengrates=priceStack 会更好哈 -->
<el-tab-pane label="价格库存" name="rates" class="rates">
<el-form ref="rates" :model="ratesForm" :rules="rules">
<el-form-item label="启用多规格">
<el-switch v-model="specSwitch" @change="changeSpecSwitch"/>
</el-form-item>
<!-- 动态添加规格属性 -->
<div v-show="ratesForm.spec === 2">
<div v-for="(specs, index) in dynamicSpec" :key="index" class="dynamic-spec">
<!-- 删除按钮 -->
<el-button type="danger" icon="el-icon-delete" circle class="spec-delete" @click="removeSpec(index)"/>
<div class="spec-header">
规格项
<el-select v-model="specs.specId" filterable placeholder="请选择" @change="changeSpec">
<el-option v-for="item in propertyPageList" :key="item.id" :label="item.name" :value="item.id"/>
</el-select>
</div>
<div class="spec-values">
<template v-for="(v, i) in specs.specValue">
<el-input v-model="v.name" class="spec-value" :key="i" disabled/>
</template>
</div>
</div>
<el-button type="primary" @click="dynamicSpec.push({specValue: []}); ratesForm.rates = []">添加规格项目</el-button>
</div>
<!-- 规格明细 -->
<el-form-item label="规格明细">
<el-table :data="ratesForm.rates" border style="width: 100%" ref="ratesTable">
<template v-if="this.specSwitch">
<el-table-column :key="index" v-for="(item, index) in dynamicSpec.filter(v => v.specName !== undefined)"
:label="item.specName">
<template v-slot="scope">
<el-input v-if="scope.row.spec" v-model="scope.row.spec[index]" disabled/>
</template>
</el-table-column>
</template>
<el-table-column label="规格图片" width="120px" :render-header="addRedStar" key="90">
<template v-slot="scope">
<ImageUpload v-model="scope.row.picUrl" :limit="1" :isShowTip="false" style="width: 100px; height: 50px"/>
</template>
</el-table-column>
<el-table-column label="市场价(元)" :render-header="addRedStar" key="92">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.marketPrice'" :rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.marketPrice"
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''"/>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="销售价(元)" :render-header="addRedStar" key="93">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.price'"
:rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.price"
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="成本价" :render-header="addRedStar" key="94">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.costPrice'"
:rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.costPrice"
oninput="value= value.match(/\d+(\.\d{0,2})?/) ? value.match(/\d+(\.\d{0,2})?/)[0] : ''" />
</el-form-item>
</template>
</el-table-column>
<el-table-column label="库存" :render-header="addRedStar" key="95">
<template v-slot="scope">
<el-form-item :prop="'rates.'+ scope.$index + '.stock'" :rules="[{required: true, trigger: 'change'}]">
<el-input v-model="scope.row.stock" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"></el-input>
</el-form-item>
</template>
</el-table-column>
<el-table-column label="预警库存" key="96">
<template v-slot="scope">
<el-input v-model="scope.row.warnStock" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"></el-input>
</template>
</el-table-column>
<el-table-column label="体积" key="97">
<template v-slot="scope">
<el-input v-model="scope.row.volume" />
</template>
</el-table-column>
<el-table-column label="重量" key="98">
<template v-slot="scope">
<el-input v-model="scope.row.weight" />
</template>
</el-table-column>
<el-table-column label="条码" key="99">
<template v-slot="scope">
<el-input v-model="scope.row.barCode" />
</template>
</el-table-column>
<template v-if="this.specSwitch">
<el-table-column fixed="right" label="操作" width="50" key="100">
<template v-slot="scope">
<el-button @click="scope.row.status = 1" type="text" size="small"
v-show="scope.row.status === undefined || scope.row.status === 0 ">禁用
</el-button>
<el-button @click="scope.row.status = 0" type="text" size="small" v-show="scope.row.status === 1">
启用
</el-button>
</template>
</el-table-column>
</template>
</el-table>
</el-form-item>
<el-form-item label="虚拟销量" prop="virtualSalesCount">
<el-input v-model="baseForm.virtualSalesCount" placeholder="请输入虚拟销量"
oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 商品详情 -->
<el-tab-pane label="商品详情" name="detail">
<el-form ref="detail" :model="baseForm" :rules="rules">
<el-form-item prop="description">
<editor v-model="baseForm.description" :min-height="380"/>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 销售设置 -->
<el-tab-pane label="高级设置" name="senior">
<el-form ref="senior" :model="baseForm" :rules="rules" label-width="100px" style="width: 95%">
<el-form-item label="排序字段">
<el-input v-model="baseForm.sort" placeholder="请输入排序字段" oninput="value=value.replace(/^(0+)|[^\d]+/g,'')"/>
</el-form-item>
<el-form-item label="是否展示库存" prop="showStock">
<el-radio-group v-model="baseForm.showStock">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
<div class="buttons">
<el-button type="info" round @click="cancel">取消</el-button>
<el-button type="success" round @click="submit">确认</el-button>
</div>
</div>
</template>
<script>
import {getBrandList} from "@/api/mall/product/brand";
import {getProductCategoryList} from "@/api/mall/product/category";
import {createSpu, getSpuDetail, updateSpu} from "@/api/mall/product/spu";
import {getPropertyListAndValue,} from "@/api/mall/product/property";
import Editor from "@/components/Editor";
import ImageUpload from "@/components/ImageUpload";
import VideoUpload from "@/components/VideoUpload";
export default {
name: "ProductSave",
components: {
Editor,
ImageUpload,
VideoUpload
},
data() {
return {
specSwitch: false,
activeName: "basic",
propName: {
checkStrictly: true,
label: "name",
value: "id",
},
// 基础设置
baseForm: {
id: null,
name: null,
sellPoint: null,
categoryIds: null,
sort: null,
description: null,
picUrls: null,
videoUrl: null,
status: 0,
virtualSalesCount: 0,
showStock: true,
brandId: null,
},
categoryList: [],
// 价格库存
ratesForm: {
spec: 1,
// 规格明细
rates: [{}]
},
dynamicSpec: [
// {
// specId: 86,
// specName: "颜色",
// specValue:[{
// name: "红色",
// id: 225,
// }]
// },
],
propertyPageList: [],
brandList: [],
specValue: null,
// 表单校验
rules: {
name: [{required: true, message: "商品名称不能为空", trigger: "blur"},],
description: [{required: true, message: "描述不能为空", trigger: "blur"},],
categoryIds: [{required: true, message: "分类id不能为空", trigger: "blur"},],
status: [{required: true, message: "商品状态不能为空", trigger: "blur"}],
brandId: [{required: true, message: "商品品牌不能为空", trigger: "blur"}],
picUrls: [{required: true, message: "商品轮播图地址不能为空", trigger: "blur"}],
},
};
},
created() {
this.getListBrand();
this.getListCategory();
this.getPropertyPageList();
const spuId = this.$route.params && this.$route.params.spuId;
if (spuId != null) {
this.updateType(spuId)
}
},
methods: {
removeSpec(index) {
this.dynamicSpec.splice(index, 1);
this.changeSpecSwitch()
},
// 必选标识
addRedStar(h, {column}) {
return [
h('span', {style: 'color: #F56C6C'}, '*'),
h('span', ' ' + column.label)
];
},
changeSpecSwitch() {
this.specSwitch ? this.ratesForm.spec = 2 : this.ratesForm.spec = 1;
this.$refs.ratesTable.doLayout();
if (this.ratesForm.spec === 1) {
this.ratesForm.rates = [{}]
} else {
this.ratesForm.rates = []
if (this.dynamicSpec.length > 0) {
this.buildRatesFormRates()
}
}
},
// 构建规格明细笛卡尔积
buildRatesFormRates() {
let rates = [];
this.dynamicSpec.map(v => v.specValue.map(m => m.name))
.reduce((last, current) => {
const array = [];
last.forEach(par1 => {
current.forEach(par2 => {
let v
// 当两个对象合并时,需使用[1,2]方式生成数组而当数组和对象合并时需使用concat
if (par1 instanceof Array) {
v = par1.concat(par2)
} else {
v = [par1, par2];
}
array.push(v)
});
});
return array;
})
.forEach(v => {
let spec = v;
// 当v为单个规格项时会变成字符串。造成表格只截取第一个字符串而不是数组的第一个元素
if (typeof v == 'string') {
spec = Array.of(v)
}
rates.push({spec: spec, status: 0, name: Array.of(v).join()})
});
this.ratesForm.rates = rates
},
/** 查询分类 */
getListCategory() {
// 执行查询
getProductCategoryList().then((response) => {
this.categoryList = this.handleTree(response.data, "id", "parentId");
});
},
/** 查询品牌列表 */
getListBrand() {
// 执行查询
getBrandList().then((response) => {
this.brandList = response.data;
});
},
// 取消按钮
cancel() {
var currentView = this.$store.state.tagsView.visitedViews[0]
for (currentView of this.$store.state.tagsView.visitedViews) {
if (currentView.path === this.$route.path) {
break
}
}
this.$store.dispatch('tagsView/delView', currentView)
.then(() => {
this.$router.push("/product/spu")
})
},
submit() {
this.$refs[this.activeName].validate((valid) => {
if (!valid) {
return;
}
let rates = JSON.parse(JSON.stringify(this.ratesForm.rates));
// 价格元转分
rates.forEach(r => {
r.marketPrice = r.marketPrice * 100;
r.price = r.price * 100;
r.costPrice = r.costPrice * 100;
})
// 动态规格调整字段
if (this.specSwitch) {
rates.forEach(r => {
let properties = []
Array.of(r.spec).forEach(s => {
let obj;
if (s instanceof Array) {
obj = s;
} else {
obj = Array.of(s);
}
obj.forEach((v, i) => {
let specValue = this.dynamicSpec[i].specValue.find(o => o.name === v);
let propertie = {};
propertie.propertyId = this.dynamicSpec[i].specId;
propertie.valueId = specValue.id;
properties.push(propertie);
})
})
r.properties = properties;
})
} else {
rates[0].name = this.baseForm.name;
rates[0].status = this.baseForm.status;
}
let form = this.baseForm
if (form.picUrls instanceof Array) {
form.picUrls = form.picUrls.flatMap(m => m.split(','))
} else if (form.picUrls.split(',') instanceof Array) {
form.picUrls = form.picUrls.split(',').flatMap(m => m.split(','))
} else {
form.picUrls = Array.of(form.picUrls)
}
form.skus = rates;
form.specType = this.ratesForm.spec;
let category = form.categoryIds instanceof Array ? form.categoryIds: Array.of(form.categoryIds)
console.log(category)
form.categoryId = category[category.length - 1];
if (form.id == null) {
createSpu(form).then(() => {
this.$modal.msgSuccess("新增成功");
}).then(()=>{
this.cancel();
})
} else {
updateSpu(form).then(() => {
this.$modal.msgSuccess("修改成功");
}).then(()=>{
this.cancel();
})
}
});
},
/** 查询规格 */
getPropertyPageList() {
// 执行查询
getPropertyListAndValue().then((response) => {
this.propertyPageList = response.data;
});
},
// 添加规格项目
changeSpec(val) {
let obj = this.propertyPageList.find(o => o.id === val);
let spec = this.dynamicSpec.find(o => o.specId === val)
spec.specId = obj.id;
spec.specName = obj.name;
spec.specValue = obj.values;
this.buildRatesFormRates();
},
updateType(id) {
getSpuDetail(id).then((response) => {
let data = response.data;
this.baseForm.id = data.id;
this.baseForm.name = data.name;
this.baseForm.sellPoint = data.sellPoint;
this.baseForm.categoryIds = data.categoryId;
this.baseForm.videoUrl = data.videoUrl;
this.baseForm.sort = data.sort;
this.baseForm.description = data.description;
this.baseForm.picUrls = data.picUrls;
this.baseForm.status = data.status;
this.baseForm.virtualSalesCount = data.virtualSalesCount;
this.baseForm.showStock = data.showStock;
this.baseForm.brandId = data.brandId;
this.ratesForm.spec = data.specType;
data.skus.forEach(r => {
r.marketPrice = this.divide(r.marketPrice, 100)
r.price = this.divide(r.price, 100)
r.costPrice = this.divide(r.costPrice, 100)
})
if (this.ratesForm.spec === 2) {
this.specSwitch = true;
data.productPropertyViews.forEach(p => {
let obj = {};
obj.specId = p.propertyId;
obj.specName = p.name;
obj.specValue = p.propertyValues;
this.dynamicSpec.push(obj);
})
data.skus.forEach(s => {
s.spec = [];
s.properties.forEach(sp => {
let spec = data.productPropertyViews.find(o => o.propertyId === sp.propertyId).propertyValues.find(v => v.id === sp.valueId).name;
s.spec.push(spec)
})
})
}
this.ratesForm.rates = data.skus
})
},
},
};
</script>
<style lang="scss">
.container{
padding: 20px;
}
.dynamic-spec {
background-color: #f2f2f2;
width: 85%;
margin: auto;
margin-bottom: 10px;
.spec-header {
padding: 30px;
padding-bottom: 20px;
.spec-name {
display: inline;
input {
width: 30%;
}
}
}
.spec-values {
width: 84%;
padding: 25px;
margin: auto;
padding-top: 5px;
.spec-value {
display: inline-block;
margin-right: 10px;
margin-bottom: 10px;
width: 13%;
}
}
.spec-delete {
float: right;
margin-top: 10px;
margin-right: 10px;
}
}
.tabs {
border-bottom: 2px solid #f2f2f2;
.el-tab-pane {
overflow-y: auto;
}
}
// 库存价格图片样式修改
.rates {
.component-upload-image {
margin: auto;
}
.el-upload--picture-card {
width: 100px;
height: 50px;
line-height: 60px;
margin: auto;
}
.el-upload-list__item {
width: 100px !important;
height: 50px !important;
}
}
.buttons {
margin-top: 20px;
height: 36px;
button {
float: right;
margin-left: 15px;
}
}
.mall-image {
.el-upload--picture-card {
width: 80px;
height: 80px;
line-height: 90px;
}
.el-upload-list__item {
width: 80px;
height: 80px;
}
}
</style>

View File

@@ -0,0 +1,164 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="会员昵称" prop="nickname">
<el-input v-model="queryParams.nickname" placeholder="请输入会员昵称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @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>
<!-- Tab 选项真正的内容在 Lab -->
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
</el-tabs>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="会员信息" align="center" prop="nickname" /> <!-- TODO 芋艿以后支持头像支持跳转 -->
<el-table-column label="优惠劵" align="center" prop="name" />
<el-table-column label="优惠券类型" align="center" prop="discountType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
</template>
</el-table-column>
<el-table-column label="领取方式" align="center" prop="takeType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_TAKE_TYPE" :value="scope.row.takeType" />
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_COUPON_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="领取时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="使用时间" align="center" prop="useTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.useTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:coupon:delete']">回收</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { deleteCoupon, getCouponPage } from "@/api/mall/promotion/coupon";
import { DICT_TYPE, getDictDatas} from "@/utils/dict";
export default {
name: "PromotionCoupon",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 优惠劵列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
createTime: [],
status: undefined,
},
// Tab 筛选
activeTab: 'all',
statusTabs: [{
label: '全部',
value: 'all'
}],
};
},
created() {
this.getList();
// 设置 statuses 过滤
for (const dict of getDictDatas(DICT_TYPE.PROMOTION_COUPON_STATUS)) {
this.statusTabs.push({
label: dict.label,
value: dict.value
})
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getCouponPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('回收将会收回会员领取的待使用的优惠券,已使用的将无法回收,确定要回收所选优惠券吗?').then(function() {
return deleteCoupon(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("回收成功");
}).catch(() => {});
},
/** tab 切换 */
tabClick(tab) {
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
this.getList();
}
}
};
</script>

View File

@@ -0,0 +1,404 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="82px">
<el-form-item label="优惠券名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入优惠劵名" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="优惠券类型" prop="discountType">
<el-select v-model="queryParams.discountType" placeholder="请选择优惠券类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</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 this.getDictDatas(DICT_TYPE.COMMON_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:coupon-template:create']">新增</el-button>
<el-button type="info" plain icon="el-icon-s-operation" size="mini"
@click="() => this.$router.push('/promotion/coupon')"
v-hasPermi="['promotion:coupon:query']">会员优惠劵</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="优惠券名称" align="center" prop="name" />
<el-table-column label="优惠券类型" align="center" prop="discountType">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_DISCOUNT_TYPE" :value="scope.row.discountType" />
</template>
</el-table-column>
<el-table-column label="优惠金额 / 折扣" align="center" prop="discount" :formatter="discountFormat" />
<el-table-column label="发放数量" align="center" prop="totalCount" />
<el-table-column label="剩余数量" align="center" prop="totalCount" :formatter="row => (row.totalCount - row.takeCount)" />
<el-table-column label="领取上限" align="center" prop="takeLimitCount" :formatter="takeLimitCountFormat" />
<el-table-column label="有效期限" align="center" prop="validityType" width="180" :formatter="validityTypeFormat" />
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<el-switch v-model="scope.row.status" :active-value="0" :inactive-value="1" @change="handleStatusChange(scope.row)"/>
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:coupon-template:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:coupon-template:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="优惠券名称" prop="name">
<el-input v-model="form.name" placeholder="请输入优惠券名称" />
</el-form-item>
<el-form-item label="优惠券类型" prop="discountType">
<el-radio-group v-model="form.discountType">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PRICE.type" label="优惠券面额" prop="discountPrice">
<el-input-number v-model="form.discountPrice" placeholder="请输入优惠金额,单位:元"
style="width: 400px" :precision="2" :min="0" />
</el-form-item>
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PERCENT.type" label="优惠券折扣" prop="discountPercent">
<el-input-number v-model="form.discountPercent" placeholder="优惠券折扣不能小于 1 折,且不可大于 9.9 折"
style="width: 400px" :precision="1" :min="1" :max="9.9" />
</el-form-item>
<el-form-item v-if="form.discountType === PromotionDiscountTypeEnum.PERCENT.type" label="最多优惠" prop="discountLimitPrice">
<el-input-number v-model="form.discountLimitPrice" placeholder="请输入最多优惠"
style="width: 400px" :precision="2" :min="0" />
</el-form-item>
<el-form-item label="满多少元可以使用" prop="usePrice">
<el-input-number v-model="form.usePrice" placeholder="无门槛请设为 0"
style="width: 400px" :precision="2" :min="0" />
</el-form-item>
<el-form-item label="领取方式" prop="takeType">
<el-radio-group v-model="form.takeType">
<el-radio :key="1" :label="1">直接领取</el-radio>
<el-radio :key="2" :label="2">指定发放</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.takeType === 1" label="发放数量" prop="totalCount">
<el-input-number v-model="form.totalCount" placeholder="发放数量,没有之后不能领取或发放,-1 为不限制"
style="width: 400px" :precision="0" :min="-1" />
</el-form-item>
<el-form-item v-if="form.takeType === 1" label="每人限领个数" prop="takeLimitCount">
<el-input-number v-model="form.takeLimitCount" placeholder="设置为 -1 时,可无限领取"
style="width: 400px" :precision="0" :min="-1" />
</el-form-item>
<el-form-item label="有效期类型" prop="validityType">
<el-radio-group v-model="form.validityType">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_COUPON_TEMPLATE_VALIDITY_TYPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.DATE.type" label="固定日期" prop="validTimes">
<el-date-picker v-model="form.validTimes" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="datetimerange"
:default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item v-if="form.validityType === CouponTemplateValidityTypeEnum.TERM.type" label="领取日期" prop="fixedStartTerm">
<el-input-number v-model="form.fixedStartTerm" placeholder="0 为今天生效"
style="width: 165px" :precision="0" :min="0"/>
<el-input-number v-model="form.fixedEndTerm" placeholder="请输入结束天数"
style="width: 165px" :precision="0" :min="0"/> 天有效
</el-form-item>
<el-form-item label="活动商品" prop="productScope">
<el-radio-group v-model="form.productScope">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
<el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
multiple filterable style="width: 400px">
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ (item.minPrice / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createCouponTemplate,
updateCouponTemplate,
deleteCouponTemplate,
getCouponTemplate,
getCouponTemplatePage,
updateCouponTemplateStatus
} from "@/api/mall/promotion/couponTemplate";
import {
CommonStatusEnum,
CouponTemplateValidityTypeEnum,
PromotionDiscountTypeEnum,
PromotionProductScopeEnum
} from "@/utils/constants";
import { getSpuSimpleList } from "@/api/mall/product/spu";
import { parseTime } from "@/utils/ruoyi";
import {changeRoleStatus} from "@/api/system/role";
export default {
name: "PromotionCouponTemplate",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 优惠劵列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
type: null,
createTime: [],
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "优惠券名称不能为空", trigger: "blur" }],
discountType: [{ required: true, message: "优惠券类型不能为空", trigger: "change" }],
discountPrice: [{ required: true, message: "优惠券面额不能为空", trigger: "blur" }],
discountPercent: [{ required: true, message: "优惠券折扣不能为空", trigger: "blur" }],
discountLimitPrice: [{ required: true, message: "最多优惠不能为空", trigger: "blur" }],
usePrice: [{ required: true, message: "满多少元可以使用不能为空", trigger: "blur" }],
takeType: [{ required: true, message: "领取方式不能为空", trigger: "change" }],
totalCount: [{ required: true, message: "发放数量不能为空", trigger: "blur" }],
takeLimitCount: [{ required: true, message: "每人限领个数不能为空", trigger: "blur" }],
validityType: [{ required: true, message: "有效期类型不能为空", trigger: "change" }],
validTimes: [{ required: true, message: "固定日期不能为空", trigger: "change" }],
fixedStartTerm: [{ required: true, message: "开始领取天数不能为空", trigger: "blur" }],
fixedEndTerm: [{ required: true, message: "开始领取天数不能为空", trigger: "blur" }],
productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
},
// 商品列表
productSpus: [],
// 如下的变量,主要为了 v-if 判断可以使用到
PromotionProductScopeEnum: PromotionProductScopeEnum,
CouponTemplateValidityTypeEnum: CouponTemplateValidityTypeEnum,
PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getCouponTemplatePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
// 查询商品列表
getSpuSimpleList().then(response => {
this.productSpus = response.data
})
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
discountType: PromotionDiscountTypeEnum.PRICE.type,
discountPrice: undefined,
discountPercent: undefined,
discountLimitPrice: undefined,
usePrice: undefined,
takeType: 1,
totalCount: undefined,
takeLimitCount: undefined,
validityType: CouponTemplateValidityTypeEnum.DATE.type,
validTimes: [],
validStartTime: undefined,
validEndTime: undefined,
fixedStartTerm: undefined,
fixedEndTerm: undefined,
productScope: PromotionProductScopeEnum.ALL.scope,
productSpuIds: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加优惠劵";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getCouponTemplate(id).then(response => {
this.form = {
...response.data,
discountPrice: response.data.discountPrice !== undefined ? response.data.discountPrice / 100.0 : undefined,
discountPercent: response.data.discountPercent !== undefined ? response.data.discountPercent / 10.0 : undefined,
discountLimitPrice: response.data.discountLimitPrice !== undefined ? response.data.discountLimitPrice / 100.0 : undefined,
usePrice: response.data.usePrice !== undefined ? response.data.usePrice / 100.0 : undefined,
validTimes: [response.data.validStartTime, response.data.validEndTime]
}
this.open = true;
this.title = "修改优惠劵";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 金额相关字段的缩放
let data = {
...this.form,
discountPrice: this.form.discountPrice !== undefined ? this.form.discountPrice * 100 : undefined,
discountPercent: this.form.discountPercent !== undefined ? this.form.discountPercent * 10 : undefined,
discountLimitPrice: this.form.discountLimitPrice !== undefined ? this.form.discountLimitPrice * 100 : undefined,
usePrice: this.form.usePrice !== undefined ? this.form.usePrice * 100 : undefined,
validStartTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[0] : undefined,
validEndTime: this.form.validTimes && this.form.validTimes.length === 2 ? this.form.validTimes[1] : undefined,
}
// 修改的提交
if (this.form.id != null) {
updateCouponTemplate(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createCouponTemplate(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 优惠劵模板状态修改 */
handleStatusChange(row) {
// 此时row 已经变成目标状态了,所以可以直接提交请求和提示
let text = row.status === CommonStatusEnum.ENABLE ? "启用" : "停用";
this.$modal.confirm('确认要"' + text + '""' + row.name + '"优惠劵吗?').then(function() {
return updateCouponTemplateStatus(row.id, row.status);
}).then(() => {
this.$modal.msgSuccess(text + "成功");
}).catch(function() {
// 异常时,需要将 row.status 状态重置回之前的
row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE
: CommonStatusEnum.ENABLE;
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除优惠劵编号为"' + id + '"的数据项?').then(function() {
return deleteCouponTemplate(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
// 格式化【优惠金额/折扣】
discountFormat(row, column) {
if (row.discountType === PromotionDiscountTypeEnum.PRICE.type) {
return `${(row.discountPrice / 100.0).toFixed(2)}`;
}
if (row.discountType === PromotionDiscountTypeEnum.PERCENT.type) {
return `${(row.discountPrice / 100.0).toFixed(2)}`;
}
return '未知【' + row.discountType + '】';
},
// 格式化【领取上限】
takeLimitCountFormat(row, column) {
if (row.takeLimitCount === -1) {
return '无领取限制';
}
return `${row.takeLimitCount} 张/人`
},
// 格式化【有效期限】
validityTypeFormat(row, column) {
if (row.validityType === CouponTemplateValidityTypeEnum.DATE.type) {
return `${parseTime(row.validStartTime)}${parseTime(row.validEndTime)}`
}
if (row.validityType === CouponTemplateValidityTypeEnum.TERM.type) {
return `领取后第 ${row.fixedStartTerm} - ${row.fixedEndTerm} 天内可用`
}
return '未知【' + row.validityType + '】';
}
}
};
</script>

View File

@@ -0,0 +1,383 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
</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 this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:discount-activity:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动时间" align="center" prop="startTime" width="240">
<template v-slot="scope">
<div>开始{{ parseTime(scope.row.startTime) }}</div>
<div>结束{{ parseTime(scope.row.endTime) }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:discount-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
scope.row.status !== PromotionActivityStatusEnum.END.type"
v-hasPermi="['promotion:discount-activity:close']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:discount-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="1000px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入活动名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
value-format="timestamp" placeholder="选择开始时间" style="width: 880px" />
</el-form-item>
<el-form-item label="商品选择">
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small"
multiple filterable style="width: 880px" @change="changeFormSku">
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name" :value="item.id">
<span style="float: left">{{ item.spuName }} &nbsp; {{ item.name}}</span>
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.price / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
<el-table v-loading="loading" :data="form.products">
<el-table-column label="商品名称" align="center" width="200">
<template v-slot="scope">
{{ scope.row.spuName }} &nbsp; {{ scope.row.name}}
</template>
</el-table-column>
<el-table-column label="商品价格" align="center" prop="price">
<template v-slot="scope">
¥{{ (scope.row.price / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="库存" align="center" prop="stock" />
<el-table-column label="优惠类型" align="center" property="discountType">
<template v-slot="scope">
<el-select v-model="scope.row.discountType" placeholder="请选择优惠类型">
<el-option v-for="dict in getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
:key="dict.value" :label="dict.label" :value="parseInt(dict.value)"/>
</el-select>
</template>
</el-table-column>
<el-table-column label="优惠" align="center" prop="startTime" width="250">
<template v-slot="scope">
<el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PRICE.type" prop="discountPrice">
减 <el-input-number v-model="scope.row.discountPrice" placeholder="请输入优惠金额"
style="width: 190px" :precision="2" :min="0" :max="scope.row.price / 100.0 - 0.01" /> 元
</el-form-item>
<el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PERCENT.type" prop="discountPercent">
打 <el-input-number v-model="scope.row.discountPercent" placeholder="请输入优惠折扣"
style="width: 190px" :precision="1" :min="1" :max="9.9" /> 折
</el-form-item>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete" @click="removeFormSku(scope.row.skuId)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createDiscountActivity,
updateDiscountActivity,
deleteDiscountActivity,
getDiscountActivity,
getDiscountActivityPage,
closeDiscountActivity
} from "@/api/mall/promotion/discountActivity";
import {
PromotionActivityStatusEnum, PromotionDiscountTypeEnum,
PromotionProductScopeEnum
} from "@/utils/constants";
import { getSkuOptionList } from "@/api/mall/product/sku";
import { deepClone } from "@/utils";
export default {
name: "PromotionDiscountActivity",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 限时折扣活动列表
list: [],
// 弹出层名称
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
createTime: [],
},
// 表单参数
form: {
skuIds: [], // 选中的 SKU
products: [], // 商品信息
},
// 表单校验
rules: {
name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
skuIds: [{ required: true, message: "选择商品不能为空", trigger: "blur" }],
},
// 商品 SKU 列表
productSkus: [],
// 如下的变量主要为了 v-if 判断可以使用到
PromotionProductScopeEnum: PromotionProductScopeEnum,
PromotionActivityStatusEnum: PromotionActivityStatusEnum,
PromotionDiscountTypeEnum: PromotionDiscountTypeEnum,
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getDiscountActivityPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
// 获得 SKU 商品列表
getSkuOptionList().then(response => {
this.productSkus = response.data;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
remark: undefined,
skuIds: [],
products: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加限时折扣活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getDiscountActivity(id).then(response => {
this.form = response.data;
// 修改数据
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.form.skuIds = response.data.products.map(item => item.skuId);
this.form.products.forEach(product => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === product.skuId);
if (!sku) {
return;
}
// 设置商品信息
product.name = sku.name;
product.spuName = sku.spuName;
product.price = sku.price;
product.stock = sku.stock;
product.discountPrice = product.discountPrice !== undefined ? product.discountPrice / 100.0 : undefined;
product.discountPercent = product.discountPercent !== undefined ? product.discountPercent / 10.0 : undefined;
});
// 打开弹窗
this.open = true;
this.title = "修改限时折扣活动";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 处理数据
const data = deepClone(this.form); // 必须深拷贝,不然后面的 products 操作会有影响
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
data.products.forEach(product => {
product.discountPrice = product.discountPrice !== undefined ? product.discountPrice * 100 : undefined;
product.discountPercent = product.discountPercent !== undefined ? product.discountPercent * 10 : undefined;
});
if (!valid) {
return;
}
// 修改的提交
if (this.form.id != null) {
updateDiscountActivity(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createDiscountActivity(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除限时折扣活动编号为"' + id + '"的数据项?').then(function() {
return deleteDiscountActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭限时折扣活动编号为"' + id + '"的数据项?').then(function() {
return closeDiscountActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => {});
},
/** 当 Form 的 SKU 发生变化时 */
changeFormSku(skuIds) {
// 处理【新增】
skuIds.forEach(skuId => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === skuId);
if (!sku) {
return;
}
// 判断已存在,直接跳过
const product = this.form.products.find(item => item.skuId === skuId);
if (product) {
return;
}
this.form.products.push({
skuId: sku.id,
name: sku.name,
price: sku.price,
stock: sku.stock,
spuId: sku.spuId,
spuName: sku.spuName,
discountType: PromotionDiscountTypeEnum.PRICE.type,
});
});
// 处理【移除】
this.form.products.map((product, index) => {
if (!skuIds.includes(product.skuId)) {
this.form.products.splice(index, 1);
}
});
},
/** 移除 Form 的 SKU */
removeFormSku(skuId) {
this.form.skuIds.map((id, index) => {
if (skuId === id) {
this.form.skuIds.splice(index, 1);
}
});
this.changeFormSku(this.form.skuIds);
},
}
}
</script>

View File

@@ -0,0 +1,306 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
</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 this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:reward-activity:create']">新增</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动时间" align="center" prop="startTime" width="240">
<template v-slot="scope">
<div>开始{{ parseTime(scope.row.startTime) }}</div>
<div>结束{{ parseTime(scope.row.endTime) }}</div>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:reward-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
scope.row.status !== PromotionActivityStatusEnum.END.type"
v-hasPermi="['promotion:reward-activity:close']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
v-hasPermi="['promotion:reward-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入活动名称" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
value-format="timestamp" placeholder="选择开始时间" style="width: 480px" />
</el-form-item>
<el-form-item label="条件类型" prop="conditionType">
<el-radio-group v-model="form.conditionType">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="优惠设置" prop="conditionType">
<!-- TODO 芋艿:待实现! -->
</el-form-item>
<el-form-item label="活动商品" prop="productScope">
<el-radio-group v-model="form.productScope">
<el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
:key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
<el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
multiple filterable style="width: 400px">
<el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import {
createRewardActivity,
updateRewardActivity,
deleteRewardActivity,
getRewardActivity,
getRewardActivityPage,
closeRewardActivity
} from "@/api/mall/promotion/rewardActivity";
import {
PromotionConditionTypeEnum,
PromotionProductScopeEnum,
PromotionActivityStatusEnum
} from "@/utils/constants";
import {getSpuSimpleList} from "@/api/mall/product/spu";
export default {
name: "PromotionRewardActivity",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 满减送活动列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
},
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
conditionType: [{ required: true, message: "条件类型不能为空", trigger: "change" }],
productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
},
// 商品列表
productSpus: [],
// 如下的变量主要为了 v-if 判断可以使用到
PromotionProductScopeEnum: PromotionProductScopeEnum,
PromotionActivityStatusEnum: PromotionActivityStatusEnum,
};
},
created() {
this.getList();
// 查询商品列表
getSpuSimpleList().then(response => {
this.productSpus = response.data
})
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getRewardActivityPage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
conditionType: PromotionConditionTypeEnum.PRICE.type,
remark: undefined,
productScope: PromotionProductScopeEnum.ALL.scope,
productSpuIds: undefined,
rules: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加满减送活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getRewardActivity(id).then(response => {
this.form = response.data;
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.open = true;
this.title = "修改满减送活动";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
this.form.startTime = this.form.startAndEndTime[0];
this.form.endTime = this.form.startAndEndTime[1];
// TODO 芋艿:临时实现
this.form.rules = [
{
limit: 1,
discountPrice: 10,
freeDelivery: true,
point: 10,
couponIds: [10, 20],
couponCounts: [1, 2]
}, {
limit: 2,
discountPrice: 20,
freeDelivery: false,
point: 20,
couponIds: [30, 40],
couponCounts: [3, 4]
}
];
// 修改的提交
if (this.form.id != null) {
updateRewardActivity(this.form).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createRewardActivity(this.form).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除满减送活动编号为"' + id + '"的数据项?').then(function() {
return deleteRewardActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => {});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭满减送活动编号为"' + id + '"的数据项?').then(function() {
return closeRewardActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => {});
}
}
};
</script>

View File

@@ -0,0 +1,491 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch"
label-width="68px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="queryParams.name" placeholder="请输入秒杀活动名称" clearable
@keyup.enter.native="handleQuery" />
</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 this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)" :key="dict.value"
:label="dict.label" :value="dict.value" />
</el-select>
</el-form-item>
<el-form-item label="参与场次" prop="timeId">
<el-select v-model="queryParams.timeId" placeholder="请选择参与场次" clearable size="small">
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss"
type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:seckill-activity:create']">新增秒杀活动</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-menu" size="mini" @click="openSeckillTime"
v-hasPermi="['promotion:seckill-activity:create']">管理参与场次</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="活动名称" align="center" prop="name" />
<el-table-column label="活动状态" align="center" prop="status">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="参与场次" prop="timeIds" width="250">
<template v-slot="scope">
<span v-for="item in seckillTimeList" :key="item.id"
v-if="scope.row.timeIds.includes(item.id)">
<el-tag style="margin:4px;" size="small">{{ item.name }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column label="活动开始时间" align="center" prop="startTime" width="190">
<template v-slot="scope">
<span>{{ "开始: " + parseTime(scope.row.startTime) }}</span>
<span>{{ "结束: " + parseTime(scope.row.endTime) }}</span>
</template>
</el-table-column>
<el-table-column label="付款订单数" align="center" prop="orderCount" />
<el-table-column label="付款人数" align="center" prop="userCount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:seckill-activity:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-close" @click="handleClose(scope.row)"
v-hasPermi="['promotion:seckill-activity:delete']">关闭</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:seckill-activity:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList" />
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="1200px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="活动名称" prop="name">
<el-input v-model="form.name" placeholder="请输入秒杀活动名称" />
</el-form-item>
<el-form-item label="活动时间" prop="startAndEndTime">
<el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange"
value-format="timestamp" range-separator="" start-placeholder="开始日期" end-placeholder="结束日期"
style="width: 1080px" />
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input-number v-model="form.sort" controls-position="right" :min="0" :max="10000">
</el-input-number>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="场次选择">
<el-select v-model="form.timeIds" placeholder="请选择参与场次" clearable size="small" multiple filterable
style="width: 880px">
<el-option v-for="item in seckillTimeList" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name + ': { ' }} {{ item.startTime }} -- {{ item.endTime +
' }'
}}</span>
<span style="float: right; color: #8492a6; font-size: 13px"></span>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="商品选择">
<el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small" multiple filterable
style="width: 880px" @change="changeFormSku">
<el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name"
:value="item.id">
<span style="float: left">{{ item.spuName }} &nbsp; {{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ (item.price /
100.0).toFixed(2)
}}</span>
</el-option>
</el-select>
<el-row>
<el-button type="primary" size="mini" @click="batchEditProduct('limitBuyCount')">限购</el-button>
<el-button type="primary" size="mini" @click="batchEditProduct('seckillPrice')">秒杀价</el-button>
<el-button type="primary" size="mini" @click="batchEditProduct('seckillStock')">秒杀库存</el-button>
</el-row>
<el-table v-loading="loading" ref="productsTable" :data="form.products">
<el-table-column type="selection" width="55">
</el-table-column>
<el-table-column label="商品名称" align="center" width="200">
<template v-slot="scope">
{{ scope.row.spuName }} &nbsp; {{ scope.row.name }}
</template>
</el-table-column>
<el-table-column label="商品价格" align="center" prop="price">
<template v-slot="scope">
{{ (scope.row.price / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column label="库存" align="center" prop="productStock" />
<el-table-column label="限购(0为不限购)" align="center" width="150">
<template v-slot="scope">
<el-input-number v-model="scope.row.limitBuyCount" size="mini" :min="0" :max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="秒杀价(元)" align="center" width="150">
<template v-slot="scope">
<el-input-number v-model="scope.row.seckillPrice" size="mini" :precision="2" :min="0"
:max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="秒杀库存" align="center" width="150" prop="seckillStock">
<template v-slot="scope">
<el-input-number v-model="scope.row.seckillStock" size="mini" :min="0" :max="10000">
</el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-delete"
@click="removeFormSku(scope.row.skuId)">删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { getSkuOptionList } from "@/api/mall/product/sku";
import { createSeckillActivity, updateSeckillActivity, closeSeckillActivity, deleteSeckillActivity, getSeckillActivity, getSeckillActivityPage, exportSeckillActivityExcel } from "@/api/mall/promotion/seckillActivity";
import { getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
import { deepClone } from "@/utils";
export default {
name: "PromotionSeckillActivity",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 秒杀活动列表
list: [],
// 秒杀场次列表
seckillTimeList: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
name: null,
status: null,
timeId: null,
createTime: [],
},
// 表单参数
form: {
skuIds: [], // 选中的 SKU
products: [], // 商品信息
timeIds: [], //选中的秒杀场次id
},
// 商品 SKU 列表
productSkus: [],
// 表单校验
rules: {
name: [{ required: true, message: "秒杀活动名称不能为空", trigger: "blur" }],
status: [{ required: true, message: "活动状态不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
sort: [{ required: true, message: "排序不能为空", trigger: "blur" }],
timeIds: [{ required: true, message: "秒杀场次不能为空", trigger: "blur" }],
totalPrice: [{ required: true, message: "订单实付金额,单位:分不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
watch: {
$route: 'getList'
},
methods: {
/** 查询列表 */
getList() {
// 从秒杀时段跳转过来并鞋带timeId参数进行查询
const timeId = this.$route.params && this.$route.params.timeId;
if (timeId) {
this.queryParams.timeId = timeId
}
this.loading = true;
// 执行查询
getSeckillActivityPage(this.queryParams).then(response => {
console.log(response.data.list, "查询出的值");
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
if (timeId) {
//查询完成后设置为空
this.$route.params.timeId = undefined
}
// 获得 SKU 商品列表
getSkuOptionList().then(response => {
this.productSkus = response.data;
});
// 获取参与场次列表
getSeckillTimeList().then(response => {
this.seckillTimeList = response.data;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
status: undefined,
remark: undefined,
startTime: undefined,
endTime: undefined,
sort: undefined,
timeIds: [],
totalPrice: undefined,
skuIds: [],
products: [],
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/**打开秒杀场次管理页面 */
openSeckillTime() {
this.$tab.openPage("秒杀场次管理", "/promotion/seckill-time");
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加秒杀活动";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getSeckillActivity(id).then(response => {
this.form = response.data;
// 修改数据
this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
this.form.skuIds = response.data.products.map(item => item.skuId);
this.form.products.forEach(product => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === product.skuId);
if (!sku) {
return;
}
// 设置商品信息
product.name = sku.name;
product.spuName = sku.spuName;
product.price = sku.price;
product.productStock = sku.stock;
this.$set(product, 'seckillStock', product.stock);
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice / 100 : undefined;
});
// 打开弹窗
this.open = true;
this.title = "修改限时折扣活动";
})
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
if (!valid) {
return;
}
// 处理数据
const data = deepClone(this.form);
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
data.products.forEach(product => {
product.stock = product.seckillStock;
product.seckillPrice = product.seckillPrice !== undefined ? product.seckillPrice * 100 : undefined;
});
// 修改的提交
if (this.form.id != null) {
updateSeckillActivity(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createSeckillActivity(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 关闭按钮操作 */
handleClose(row) {
const id = row.id;
this.$modal.confirm('是否确认关闭秒杀活动编号为"' + id + '"的数据项?').then(function () {
return closeSeckillActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("关闭成功");
}).catch(() => { });
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除秒杀活动编号为"' + id + '"的数据项?').then(function () {
return deleteSeckillActivity(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
/** 批量修改商品秒杀价,秒杀库存,每人限购数量 */
batchEditProduct(editType) {
const selectProducts = this.$refs.productsTable.selection;
if (selectProducts.length === 0) {
this.$modal.msgError("请选择需要修改的商品");
return;
}
let promptTitle = '请输入';
let regularPattern = /^[\s\S]*.*[^\s][\s\S]*$/; // 判断非空,且非空格
//限购数
if (editType === 'limitBuyCount') {
promptTitle = '限购数';
regularPattern = /^[0-9]*$/; //数字
}
//秒杀价
if (editType === 'seckillPrice') {
promptTitle = '秒杀价(元)';
regularPattern = /^[0-9]+(\.[0-9]{1,2})?$/; // 有一位或两位小数的正数
}
//秒杀库存
if (editType === 'seckillStock') {
promptTitle = '秒杀库存';
regularPattern = /^[0-9]*$/; //数字
}
this.$prompt(promptTitle, '提示', {
confirmButtonText: '保存',
cancelButtonText: '取消',
inputPattern: regularPattern,
inputErrorMessage: promptTitle + '格式不正确'
}).then(({ value }) => {
if (editType === 'limitBuyCount') {
selectProducts.forEach((item) => {
item.limitBuyCount = value;
})
}
if (editType === 'seckillPrice') {
selectProducts.forEach((item) => {
item.seckillPrice = value;
})
}
if (editType === 'seckillStock') {
selectProducts.forEach((item) => {
item.seckillStock = value;
})
}
}).catch();
},
/** 当 Form 的 SKU 发生变化时 */
changeFormSku(skuIds) {
// 处理【新增】
skuIds.forEach(skuId => {
// 获得对应的 SKU 信息
const sku = this.productSkus.find(item => item.id === skuId);
if (!sku) {
return;
}
// 判断已存在,直接跳过
const product = this.form.products.find(item => item.skuId === skuId);
if (product) {
return;
}
this.form.products.push({
skuId: sku.id,
name: sku.name,
price: sku.price,
productStock: sku.stock,
spuId: sku.spuId,
spuName: sku.spuName,
limitBuyCount: 1,
seckillStock: sku.stock,
seckillPrice: sku.price,
});
});
// 处理【移除】
this.form.products.map((product, index) => {
if (!skuIds.includes(product.skuId)) {
this.form.products.splice(index, 1);
}
});
},
/** 移除 Form 的 SKU */
removeFormSku(skuId) {
this.form.skuIds.map((id, index) => {
if (skuId === id) {
this.form.skuIds.splice(index, 1);
}
});
this.changeFormSku(this.form.skuIds);
},
}
};
</script>

View File

@@ -0,0 +1,198 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
v-hasPermi="['promotion:seckill-time:create']">新增秒杀时段</el-button>
</el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="秒杀时段名称" align="center" prop="name" />
<el-table-column label="开始时间点" align="center" prop="startTime" width="180">
<template v-slot="scope">
<span>{{ scope.row.startTime }}</span>
</template>
</el-table-column>
<el-table-column label="结束时间点" align="center" prop="endTime" width="180">
<template v-slot="scope">
<span>{{ scope.row.endTime }}</span>
</template>
</el-table-column>
<el-table-column label="秒杀活动数量" align="center" prop="seckillActivityCount" />
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleOpenSeckillActivity(scope.row)">
查看秒杀活动</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
v-hasPermi="['promotion:seckill-time:update']">修改</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
v-hasPermi="['promotion:seckill-time:delete']">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 对话框(添加 / 修改) -->
<el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
<el-form ref="form" :model="form" :rules="rules" label-width="140px">
<el-form-item label="秒杀场次名称" prop="name">
<el-input v-model="form.name" placeholder="请输入秒杀时段名称" clearable />
</el-form-item>
<el-form-item label="秒杀时间段" prop="startAndEndTime">
<el-time-picker is-range v-model="form.startAndEndTime" range-separator="至" start-placeholder="开始时间"
end-placeholder="结束时间" placeholder="选择时间范围" value-format="HH:mm:ss">
</el-time-picker>
</el-form-item>
</el-form>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import { createSeckillTime, updateSeckillTime, deleteSeckillTime, getSeckillTime, getSeckillTimePage, exportSeckillTimeExcel, getSeckillTimeList } from "@/api/mall/promotion/seckillTime";
import router from "@/router";
import { deepClone } from "@/utils";
export default {
name: "PromotionSeckillTime",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
// total: 0,
// 秒杀时段列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 表单参数
form: {},
// 表单校验
rules: {
name: [{ required: true, message: "秒杀时段名称不能为空", trigger: "blur" }],
startAndEndTime: [{ required: true, message: "秒杀时间段不能为空", trigger: "blur" }],
}
};
},
created() {
this.getList();
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getSeckillTimeList().then(response => {
this.list = response.data;
this.loading = false;
});
},
/** 取消按钮 */
cancel() {
this.open = false;
this.reset();
},
/** 表单重置 */
reset() {
this.form = {
id: undefined,
name: undefined,
startAndEndTime: undefined,
startTime: undefined,
endTime: undefined,
};
this.resetForm("form");
},
/** 搜索按钮操作 */
handleQuery() {
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/**查看当前秒杀时段的秒杀活动 */
handleOpenSeckillActivity(row) {
router.push({ name: 'SeckillActivity', params: { timeId: row.id } })
},
/** 新增按钮操作 */
handleAdd() {
this.reset();
this.open = true;
this.title = "添加秒杀时段";
},
/** 修改按钮操作 */
handleUpdate(row) {
this.reset();
const id = row.id;
getSeckillTime(id).then(response => {
response.data.startAndEndTime = [response.data.startTime, response.data.endTime]
this.form = response.data;
this.open = true;
this.title = "修改秒杀时段";
});
},
/** 提交按钮 */
submitForm() {
this.$refs["form"].validate(valid => {
console.log(valid, "是否通过");
if (!valid) {
return;
}
// 处理数据
const data = deepClone(this.form);
data.startTime = this.form.startAndEndTime[0];
data.endTime = this.form.startAndEndTime[1];
// 修改的提交
if (this.form.id != null) {
updateSeckillTime(data).then(response => {
this.$modal.msgSuccess("修改成功");
this.open = false;
this.getList();
});
return;
}
// 添加的提交
createSeckillTime(data).then(response => {
this.$modal.msgSuccess("新增成功");
this.open = false;
this.getList();
});
});
},
/** 删除按钮操作 */
handleDelete(row) {
const id = row.id;
this.$modal.confirm('是否确认删除秒杀时段编号为"' + id + '"的数据项?').then(function () {
return deleteSeckillTime(id);
}).then(() => {
this.getList();
this.$modal.msgSuccess("删除成功");
}).catch(() => { });
},
}
};
</script>

View File

@@ -0,0 +1,229 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-form-item label="商品名称" prop="spuName">
<el-input v-model="queryParams.spuName" placeholder="请输入商品 SPU 名称" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="退款编号" prop="no">
<el-input v-model="queryParams.no" placeholder="请输入退款编号" clearable @keyup.enter.native="handleQuery"/>
</el-form-item>
<el-form-item label="订单编号" prop="orderNo">
<el-input v-model="queryParams.orderNo" placeholder="请输入订单编号" clearable @keyup.enter.native="handleQuery"/>
</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 this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="售后方式" prop="way">
<el-select v-model="queryParams.way" placeholder="请选择售后方式" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_WAY)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="售后类型" prop="type">
<el-select v-model="queryParams.type" placeholder="请选择售后类型" clearable size="small">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" @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>
<!-- Tab 选项真正的内容在 Table -->
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick" style="margin-top: -40px;">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value" />
</el-tabs>
<!-- 列表 -->
<el-table v-loading="loading" :data="list">
<el-table-column label="退款编号" align="center" prop="no" />
<el-table-column label="订单编号" align="center" prop="orderNo" /> <!-- TODO 芋艿未来要加个订单链接 -->
<el-table-column label="商品信息" align="center" prop="spuName" width="auto" min-width="300">
<!-- TODO @小红样式不太对辛苦改改 -->
<!-- <div v-slot="{ row }" class="goods-info">-->
<!-- <img :src="row.picUrl"/>-->
<!-- <span class="ellipsis-2" :title="row.name">{{row.name}}</span>-->
<!-- </div>-->
</el-table-column>
<el-table-column label="订单金额" align="center" prop="refundPrice">
<template v-slot="scope">
<span>{{ (scope.row.refundPrice / 100.0).toFixed(2) }}</span>
</template>
</el-table-column>
<el-table-column label="买家" align="center" prop="user.nickname" /> <!-- TODO 芋艿未来要加个会员链接 -->
<el-table-column label="申请时间" align="center" prop="createTime" width="180">
<template v-slot="scope">
<span>{{ parseTime(scope.row.createTime) }}</span>
</template>
</el-table-column>
<el-table-column label="售后状态" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="售后方式" align="center">
<template v-slot="scope">
<dict-tag :type="DICT_TYPE.TRADE_AFTER_SALE_WAY" :value="scope.row.way" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template v-slot="scope">
<el-button size="mini" type="text" icon="el-icon-thumb"
>处理退款</el-button>
<!-- @click="handleUpdate(scope.row)" v-hasPermi="['trade:after-sale:update']"-->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getAfterSalePage } from "@/api/mall/trade/afterSale";
import { datePickerOptions } from "@/utils/constants";
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
export default {
name: "TradeAfterSale",
components: {
},
data() {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 交易售后列表
list: [],
// 弹出层标题
title: "",
// 是否显示弹出层
open: false,
// 查询参数
queryParams: {
pageNo: 1,
pageSize: 10,
no: null,
status: null,
orderNo: null,
spuName: null,
createTime: [],
way: null,
type: null,
},
// Tab 筛选
activeTab: 'all',
statusTabs: [{
label: '全部',
value: 'all'
}],
// 静态变量
datePickerOptions: datePickerOptions
};
},
created() {
this.getList();
// 设置 statuses 过滤
for (const dict of getDictDatas(DICT_TYPE.TRADE_AFTER_SALE_STATUS)) {
this.statusTabs.push({
label: dict.label,
value: dict.value
})
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getAfterSalePage(this.queryParams).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.activeTab = 'all'; // 处理 tab
this.handleQuery();
},
/** tab 切换 */
tabClick(tab) {
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
this.getList();
},
goToDetail (row) {
this.$router.push({ path: '/mall/trade/order/detail', query: { orderNo: row.orderNo }})
}
}
};
</script>
<style lang="scss" scoped>
::v-deep .table-wrapper {
.el-table__row{
.el-table__cell {
border-bottom: none;
.cell{
.el-table {
.el-table__row {
>.el-table__cell {
.goods-info{
display: flex;
img{
margin-right: 10px;
width: 60px;
height: 60px;
border: 1px solid #e2e2e2;
}
}
.ellipsis-2 {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-line-clamp: 2; /* 要显示的行数 */
-webkit-box-orient: vertical;
word-break: break-all;
line-height: 22px !important;
max-height: 44px !important;
}
}
}
}
}
}
}
}
</style>

View File

@@ -0,0 +1,279 @@
<template>
<div class="app-container order-detail-page">
<!-- 订单信息 -->
<el-descriptions title="订单信息">
<el-descriptions-item label="订单号">{{ order.no }}</el-descriptions-item>
<el-descriptions-item label="配送方式">物流配送</el-descriptions-item> <!-- TODO 芋艿待实现 -->
<el-descriptions-item label="营销活动">物流配送</el-descriptions-item> <!-- TODO 芋艿待实现 -->
<el-descriptions-item label="订单类型">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_TYPE" :value="order.type" />
</el-descriptions-item>
<el-descriptions-item label="收货人">{{ order.receiverName }}</el-descriptions-item>
<el-descriptions-item label="买家留言">{{ order.userRemark }}</el-descriptions-item>
<el-descriptions-item label="订单来源">
<dict-tag :type="DICT_TYPE.TERMINAL" :value="order.terminal" />
</el-descriptions-item>
<el-descriptions-item label="联系电话">{{ order.receiverMobile }}</el-descriptions-item>
<el-descriptions-item label="商家备注">{{ order.remark }}</el-descriptions-item>
<el-descriptions-item label="支付单号">{{ order.payOrderId }}</el-descriptions-item>
<el-descriptions-item label="付款方式">
<dict-tag :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="order.payChannelCode" />
</el-descriptions-item>
<el-descriptions-item label="买家">{{ order.user.nickname }}</el-descriptions-item> <!-- TODO 芋艿待实现跳转会员 -->
<el-descriptions-item label="收货地址">
{{ order.receiverAreaName }} &nbsp; {{ order.receiverDetailAddress }} &nbsp;
<el-link v-clipboard:copy="order.receiverAreaName + ' ' + order.receiverDetailAddress"
v-clipboard:success="clipboardSuccess" icon="el-icon-document-copy" type="primary"/>
</el-descriptions-item>
</el-descriptions>
<!-- 订单状态 -->
<el-descriptions title="订单状态" :column="1">
<el-descriptions-item label="订单状态">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="order.status" />
</el-descriptions-item>
<el-descriptions-item label-class-name="no-colon">
<el-button type="primary" size="small">调整价格</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">备注</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">发货</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">关闭订单</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">修改地址</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">打印电子面单</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">打印发货单</el-button> <!-- TODO 芋艿待实现 -->
<el-button type="primary" size="small">确认收货</el-button> <!-- TODO 芋艿待实现 -->
</el-descriptions-item>
<el-descriptions-item label="提醒" label-style="color: red">
买家付款成功后货款将直接进入您的商户号微信支付宝<br />
请及时关注你发出的包裹状态确保可以配送至买家手中 <br />
如果买家表示没收到货或货物有问题请及时联系买家处理友好协商
</el-descriptions-item>
</el-descriptions>
<!-- 物流信息 TODO -->
<!-- 商品信息 -->
<el-descriptions title="商品信息" :column="6">
<el-descriptions-item labelClassName="no-colon">
<el-table :data="order.items" border>
<el-table-column prop="spuName" label="商品" width="700">
<template v-slot="{ row }">
{{row.spuName}}
<el-tag size="medium" v-for="property in row.properties" :key="property.propertyId">
{{property.propertyName}}{{property.valueName}}</el-tag>
</template>
</el-table-column>
<el-table-column prop="originalUnitPrice" label="单价(元)" width="180">
<template v-slot="{ row }">
{{ (row.originalUnitPrice / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="count" label="数量" width="180"/>
<el-table-column prop="originalPrice" label="小计(元)" width="180">
<template v-slot="{ row }">
{{ (row.originalPrice / 100.0).toFixed(2) }}
</template>
</el-table-column>
<el-table-column prop="afterSaleStatus" label="退款状态">
<template v-slot="{ row }">
<dict-tag :type="DICT_TYPE.TRADE_ORDER_ITEM_AFTER_SALE_STATUS" :value="row.afterSaleStatus" />
</template>
</el-table-column>
</el-table>
</el-descriptions-item>
<el-descriptions-item v-for="(item,index) in 5" label-class-name="no-colon" :key="item" /> <!-- 占位 -->
<el-descriptions-item label="商品总额">{{ (order.originalPrice / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="运费金额">{{ (order.deliveryPrice / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="订单调价">{{ (order.adjustPrice / 100.0).toFixed(2) }}</el-descriptions-item>
<el-descriptions-item label="商品优惠" label-style="color: red">
{{ ((order.originalPrice - order.originalPrice) / 100.0).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item label="订单优惠" label-style="color: red">
{{ (order.discountPrice / 100.0).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item label="积分抵扣" label-style="color: red">
{{ (order.pointPrice / 100.0).toFixed(2) }}
</el-descriptions-item>
<el-descriptions-item v-for="(item,index) in 5" label-class-name="no-colon" :key="item" /> <!-- 占位 -->
<el-descriptions-item label="应付金额">
{{ (order.payPrice / 100.0).toFixed(2) }}
</el-descriptions-item>
</el-descriptions>
<template v-for="(group, index) in detailGroups">
<el-descriptions v-bind="group.groupProps" :key="`group_${index}`" :title="group.title">
<!-- 订单操作日志 -->
<el-descriptions-item v-if="group.key === 'orderLog'" labelClassName="no-colon">
<el-timeline>
<el-timeline-item
v-for="(activity, index) in detailInfo[group.key]"
:key="index"
:timestamp="activity.timestamp"
>
{{activity.content}}
</el-timeline-item>
</el-timeline>
</el-descriptions-item>
<!-- 物流信息 -->
<el-descriptions-item v-if="group.key === 'expressInfo'" labelClassName="no-colon">
<el-tabs type="card">
<!-- 循环包裹物流信息 -->
<el-tab-pane v-for="(pkgInfo, pInIdx) in detailInfo[group.key]" :key="`pkgInfo_${pInIdx}`" :label="pkgInfo.label">
<!-- 包裹详情 -->
<el-descriptions>
<el-descriptions-item v-for="(pkgChild, pkgCIdx) in group.children" v-bind="pkgChild.childProps" :key="`pkgChild_${pkgCIdx}`" :label="pkgChild.label">
<!-- 包裹商品列表 -->
<template v-if="pkgChild.valueKey === 'goodsList' && pkgInfo[pkgChild.valueKey]">
<div v-for="(goodInfo, goodInfoIdx) in pkgInfo[pkgChild.valueKey]" :key="`goodInfo_${goodInfoIdx}`" style="display: flex;">
<el-image
style="width: 100px;height: 100px;flex: none"
:src="goodInfo.imgUrl">
</el-image>
<el-descriptions :column="1">
<el-descriptions-item labelClassName="no-colon">{{goodInfo.name}}</el-descriptions-item>
<el-descriptions-item label="数量">{{goodInfo.count}}</el-descriptions-item>
</el-descriptions>
</div>
</template>
<!-- 包裹物流详情 -->
<el-timeline v-else-if="pkgChild.valueKey==='wlxq'">
<el-timeline-item
v-for="(activity, index) in pkgInfo[pkgChild.valueKey]"
:key="index"
:timestamp="activity.timestamp"
>
{{activity.content}}
</el-timeline-item>
</el-timeline>
<template v-else>
{{pkgInfo[pkgChild.valueKey]}}
</template>
</el-descriptions-item>
</el-descriptions>
</el-tab-pane>
</el-tabs>
</el-descriptions-item>
</el-descriptions>
</template>
</div>
</template>
<script>
import { getOrderDetail } from "@/api/mall/trade/order";
export default {
name: "TradeOrderDetail",
data () {
return {
detailGroups: [
{
title: '物流信息',
key: 'expressInfo',
children: [
{ label: '发货时间', valueKey: 'fhsj'},
{ label: '物流公司', valueKey: 'wlgs'},
{ label: '运单号', valueKey: 'ydh'},
{ label: '物流状态', valueKey: 'wlzt', childProps: { span: 3 }},
{ label: '物流详情', valueKey: 'wlxq'}
]
},
{
title: '订单操作日志',
key: 'orderLog'
}
],
detailInfo: {
expressInfo: [ // 物流信息
{
label: '包裹1',
name: 'bg1',
fhsj: '2022-11-03 16:50:45',
wlgs: '极兔',
ydh: '2132123',
wlzt: '不支持此快递公司',
wlxq: [
{
content: '正在派送途中,请您准备签收(派件人:王涛,电话:13854563814)',
timestamp: '2018-04-15 15:00:16'
},
{
content: '快件到达 【烟台龙口东江村委营业点】',
timestamp: '2018-04-13 14:54:19'
},
{
content: '快件已发车',
timestamp: '2018-04-11 12:55:52'
},
{
content: '快件已发车',
timestamp: '2018-04-11 12:55:52'
},
{
content: '快件已发车',
timestamp: '2018-04-11 12:55:52'
}
]
}
],
orderLog: [ // 订单操作日志
{
content: '买家【乌鸦】关闭了订单',
timestamp: '2018-04-15 15:00:16'
},
{
content: '买家【乌鸦】下单了',
timestamp: '2018-04-15 15:00:16'
}
],
goodsInfo: [] // 商品详情tableData
},
order: {
items: [],
user: {},
},
}
},
created() {
getOrderDetail(this.$route.query.id).then(res => {
this.order = res.data
})
},
methods: {
clipboardSuccess() {
this.$modal.msgSuccess("复制成功");
}
}
}
</script>
<style lang="scss" scoped>
:deep(.el-descriptions){
&:not(:nth-child(1)) {
margin-top: 20px;
}
.el-descriptions__title{
display: flex;
align-items: center;
&::before{
content: '';
display: inline-block;
margin-right: 10px;
width: 3px;
height: 20px;
background-color: #409EFF;
}
}
.el-descriptions-item__container{
margin: 0 10px;
.no-colon{
margin: 0;
&::after{
content: ''
}
}
}
}
</style>

View File

@@ -0,0 +1,281 @@
<template>
<div class="app-container">
<doc-alert title="功能开启" url="https://doc.iocoder.cn/mall/build/" />
<!-- 搜索工作栏 -->
<!-- TODO: inline 看看是不是需要; v-show= 那块逻辑还是要的 -->
<el-row :gutter="20">
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
<el-col :span="6" :xs="24">
<el-form-item label="搜索方式" prop="searchValue">
<el-input v-model="queryParams.searchValue" style="width: 240px">
<el-select v-model="queryParams.searchType" slot="prepend" style="width: 100px">
<el-option v-for="dict in searchTypes" :key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-input>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单类型" prop="type">
<el-select v-model="queryParams.type" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单状态" prop="status">
<el-select v-model="queryParams.status" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="订单来源" prop="terminal">
<el-select v-model="queryParams.terminal" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.TERMINAL)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="支付方式" prop="payChannelCode">
<el-select v-model="queryParams.payChannelCode" clearable style="width: 240px">
<el-option v-for="dict in this.getDictDatas(DICT_TYPE.PAY_CHANNEL_CODE_TYPE)"
:key="dict.value" :label="dict.label" :value="dict.value"/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="6" :xs="24">
<el-form-item label="下单时间" prop="createTime">
<el-date-picker v-model="queryParams.createTime" style="width: 240px" value-format="yyyy-MM-dd HH:mm:ss" type="daterange"
range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"
:picker-options="datePickerOptions" :default-time="['00:00:00', '23:59:59']" />
</el-form-item>
</el-col>
<el-col :span="6" :xs="24" style="line-height: 32px">
<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-col>
</el-form>
</el-row>
<!-- 操作工具栏 -->
<el-row :gutter="10" class="mb8">
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row>
<!-- tab切换 -->
<!-- TODO @小程看看能不能往上挪 -40px隐藏搜索刷新对齐 -->
<el-tabs v-model="activeTab" type="card" @tab-click="tabClick">
<el-tab-pane v-for="tab in statusTabs" :key="tab.value" :label="tab.label" :name="tab.value">
<!-- 列表 -->
<el-table v-loading="loading" :data="list" :show-header="false" class="order-table">
<el-table-column>
<template v-slot="{ row }">
<el-row type="flex" align="middle">
<el-col :span="5">
订单号{{row.no}}
<el-popover title="支付单号:" :content="row.payOrderId + ''" placement="right" width="200" trigger="click">
<el-button slot="reference" type="text">更多</el-button>
</el-popover>
</el-col>
<el-col :span="5">下单时间{{ parseTime(row.createTime) }}</el-col>
<el-col :span="4">订单来源
<dict-tag :type="DICT_TYPE.TERMINAL" :value="row.terminal" />
</el-col>
<el-col :span="4">支付方式
<dict-tag v-if="row.payChannelCode" :type="DICT_TYPE.PAY_CHANNEL_CODE_TYPE" :value="row.payChannelCode" />
<span v-else>未支付</span>
</el-col>
<el-col :span="6" align="right">
<el-button type="text" @click="goToDetail(row)">详情</el-button>
</el-col>
</el-row>
<!-- 订单下的商品 -->
<el-table :data="row.items" border :show-header="true">
<el-table-column label="商品" prop="goods" header-align="center" width="auto" min-width="300">
<template v-slot="{ row, $index }">
<div class="goods-info">
<img :src="row.picUrl"/>
<span class="ellipsis-2" :title="row.spuName">{{row.spuName}}</span>
<!-- TODO @小程下面是商品属性想当度一行放在商品名下面 -->
<el-tag size="medium" v-for="property in row.properties" :key="property.propertyId">
{{property.propertyName}}{{property.valueName}}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column label="单价(元)/数量" prop="fee" align="center" width="115">
<template v-slot="{ row }">
<div>{{ (row.originalUnitPrice / 100.0).toFixed(2) }}</div>
<div>{{row.count}} </div>
</template>
</el-table-column>
<!-- TODO @小程这里应该是一个订单下多个商品只展示订单上的总金额就是 order.payPrice -->
<el-table-column label="实付金额(元)" prop="amount" align="center" width="100"/>
<!-- TODO @小程这里应该是一个订单下多个商品只展示订单上的收件信息使用 order.receiverXXX 开头的字段 -->
<el-table-column label="买家/收货人" prop="buyer" header-align="center" width="auto" min-width="300">
<template v-slot="{ row }">
<!-- TODO @芋艿以后增加一个会员详情界面 -->
<div>{{row.buyer}}</div>
<div>{{row.receiver}}{{row.tel}}</div>
<div class="ellipsis-2" :title="row.address">{{row.address}}</div>
</template>
</el-table-column>
<!-- TODO @小程这里应该是一个订单下多个商品交易状态是统一的使用 order.status 字段 -->
<el-table-column label="交易状态" prop="status" align="center" width="100"/>
</el-table>
</template>
</el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<!-- 分页组件 -->
<pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
@pagination="getList"/>
</div>
</template>
<script>
import { getOrderPage } from "@/api/mall/trade/order";
import { datePickerOptions } from "@/utils/constants";
import { DICT_TYPE, getDictDatas } from "@/utils/dict";
export default {
name: "TradeOrder",
data () {
return {
// 遮罩层
loading: true,
// 导出遮罩层
exportLoading: false,
// 显示搜索条件
showSearch: true,
// 总条数
total: 0,
// 交易售后列表
list: [],
queryParams: {
pageNo: 1,
pageSize: 10,
searchType: 'no',
searchValue: '',
type: null,
status: null,
payChannelCode: null,
createTime: [],
},
// Tab 筛选
activeTab: 'all',
statusTabs: [{
label: '全部',
value: 'all'
}],
// 静态变量
datePickerOptions: datePickerOptions,
searchTypes: [
{ label: '订单号', value: 'no' },
{ label: '会员编号', value: 'userId' },
{ label: '会员昵称', value: 'userNickname' },
{ label: '会员手机号', value: 'userMobile' },
{ label: '收货人姓名', value: 'receiverName' },
{ label: '收货人手机号码', value: 'receiverMobile' },
],
}
},
created() {
this.getList();
// 设置 statuses 过滤
for (const dict of getDictDatas(DICT_TYPE.TRADE_ORDER_STATUS)) {
this.statusTabs.push({
label: dict.label,
value: dict.value
})
}
},
methods: {
/** 查询列表 */
getList() {
this.loading = true;
// 执行查询
getOrderPage({
...this.queryParams,
searchType: undefined,
searchValue: undefined,
no: this.queryParams.searchType === 'no' ? this.queryParams.searchValue : undefined,
userId: this.queryParams.searchType === 'userId' ? this.queryParams.searchValue : undefined,
userNickname: this.queryParams.searchType === 'userNickname' ? this.queryParams.searchValue : undefined,
userMobile: this.queryParams.searchType === 'userMobile' ? this.queryParams.searchValue : undefined,
receiverName: this.queryParams.searchType === 'receiverName' ? this.queryParams.searchValue : undefined,
receiverMobile: this.queryParams.searchType === 'receiverMobile' ? this.queryParams.searchValue : undefined,
}).then(response => {
this.list = response.data.list;
this.total = response.data.total;
this.loading = false;
});
},
/** 搜索按钮操作 */
handleQuery() {
this.queryParams.pageNo = 1;
this.activeTab = this.queryParams.status ? this.queryParams.status : 'all'; // 处理 tab
this.getList();
},
/** 重置按钮操作 */
resetQuery() {
this.resetForm("queryForm");
this.handleQuery();
},
/** tab 切换 */
tabClick(tab) {
this.queryParams.status = tab.name === 'all' ? undefined : tab.name;
this.getList();
},
goToDetail (row) {
this.$router.push({ name: 'TradeOrderDetail', query: { id: row.id }})
}
}
}
</script>
<style lang="scss" scoped>
::v-deep .order-table{
border-bottom: none;
&::before{
height: 0;
}
.el-table__row{
.el-table__cell{
border-bottom: none;
.cell{
.el-table {
.el-table__row{
>.el-table__cell{
.goods-info{
display: flex;
img{
margin-right: 10px;
width: 60px;
height: 60px;
border: 1px solid #e2e2e2;
}
}
.ellipsis-2{
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
-webkit-line-clamp: 2; /* 要显示的行数 */
-webkit-box-orient: vertical;
word-break: break-all;
line-height: 22px !important;
max-height: 44px !important;
}
}
}
}
}
}
}
}
</style>