refactor: 四端完成 Options API → script setup + vuex → pinia 迁移

- buyer(95) / seller(169) / im(53) / manager(280) 共 597 个组件全部从 Options API 转换为 <script setup>
- 四端 vuex 全部移除,状态管理统一迁移至 pinia 4(stores/ 目录)
- util.js 等基础设施去除 vm 首参依赖;全局属性($Message/$filters 等)在 script 内改为具名导入
- 被外部 ref 访问的公共组件均已补齐 defineExpose(清单见各端 MIGRATION-VUE3.md)
- 各端 eslint 启用 vue/component-api-style 防止新代码回退 Options API
- 四端 yarn build 全部通过;迁移约定与注意事项已更新至各端 MIGRATION-VUE3.md
This commit is contained in:
lifenlong
2026-10-13 07:06:59 +08:00
parent 870bacdd8c
commit 88fc1b0b2a
674 changed files with 71220 additions and 69943 deletions

View File

@@ -2,7 +2,7 @@
<div class="search">
<el-card>
<el-form
ref="searchForm"
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
@@ -216,7 +216,7 @@
</el-card>
<el-dialog v-model="modalVisible" title="下架操作" width="500px" :close-on-click-modal="false">
<el-form ref="underForm" :model="underForm" label-width="100px">
<el-form ref="underFormRef" :model="underForm" label-width="100px">
<el-form-item label="下架原因" prop="reason">
<el-input v-model="underForm.reason" clearable />
</el-form-item>
@@ -255,7 +255,7 @@
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="batchAuditForm" :model="batchAuditForm" label-width="100px">
<el-form ref="batchAuditFormRef" :model="batchAuditForm" label-width="100px">
<el-form-item label="审核结果" prop="auth_flag">
<el-radio-group v-model="batchAuditForm.auth_flag">
<el-radio :value="1">审核通过</el-radio>
@@ -278,7 +278,7 @@
</el-dialog>
<el-dialog v-model="goodsGroupFlag" title="批量设置商品分组" width="420px">
<el-form ref="goodsGroupForm" :model="goodsGroupForm" :rules="goodsGroupRule" label-width="90px">
<el-form ref="goodsGroupFormRef" :model="goodsGroupForm" :rules="goodsGroupRule" label-width="90px">
<el-form-item label="商品分组" prop="groupId">
<el-select v-model="goodsGroupForm.groupId" clearable filterable style="width: 240px">
<el-option
@@ -298,7 +298,8 @@
</div>
</template>
<script>
<script setup>
import { computed, nextTick, onMounted, reactive, ref } from "vue";
import {
getGoodsListData,
getGoodsNumerData,
@@ -310,421 +311,423 @@ import {
} from "@/api/goods";
import { ElMessage, ElMessageBox } from "element-plus";
import { unitPrice, customRouterPush } from "@/utils/filters";
export default {
name: "goods",
data() {
return {
id: "", //要操作的id
loading: true, // 表单加载状态
modalVisible: false, // 添加或编辑显示
searchForm: {
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "create_time", // 默认排序字段
order: "desc", // 默认排序方式
groupId: "",
},
underForm: {
// 下架原因
reason: "",
},
goodsAuditForm: {
// 商品审核表单
auth_flag: 1,
},
auditModalVisible: false, // 审核弹框显示状态
auditSubmitLoading: false,
currentAuditGoods: null, // 当前审核的商品
submitLoading: false, // 添加或编辑提交状态
data: [], // 表单数据
total: 0, // 表单数据总数
currentStatus: "ALL",
goodsNumerData: {},
selectedRows: [], // 选中的行数据
selectAll: false, // 全选状态
batchAuditModalVisible: false, // 批量审核弹框显示状态
batchAuditSubmitLoading: false,
batchAuditForm: {
auth_flag: 1,
reason: ''
},
goodsGroupFlag: false,
goodsGroupLoading: false,
goodsGroupList: [],
goodsGroupForm: {
groupId: ""
},
goodsGroupRule: {
groupId: [{ required: true, message: "请选择商品分组", trigger: "change" }]
}
defineOptions({ name: "goods" });
// 模板 ref(searchFormRef/underFormRef/batchAuditFormRef/goodsGroupFormRef 因与 data 同名改为 xxxRef)
const searchFormRef = ref(null);
const table = ref(null);
const underFormRef = ref(null);
const auditForm = ref(null);
const batchAuditFormRef = ref(null);
const goodsGroupFormRef = ref(null);
const id = ref(""); //要操作的id
const loading = ref(true); // 表单加载状态
const modalVisible = ref(false); // 添加或编辑显示
const searchForm = reactive({
// 搜索框初始化对象
pageNumber: 1, // 当前页数
pageSize: 20, // 页面大小
sort: "create_time", // 默认排序字段
order: "desc", // 默认排序方式
groupId: "",
});
const underForm = reactive({
// 下架原因
reason: "",
});
const goodsAuditForm = reactive({
// 商品审核表单
auth_flag: 1,
});
const auditModalVisible = ref(false); // 审核弹框显示状态
const auditSubmitLoading = ref(false);
const currentAuditGoods = ref(null); // 当前审核的商品
const submitLoading = ref(false); // 添加或编辑提交状态
const data = ref([]); // 表单数据
const total = ref(0); // 表单数据总数
const currentStatus = ref("ALL");
const goodsNumerData = ref({});
const selectedRows = ref([]); // 选中的行数据
const selectAll = ref(false); // 全选状态
const batchAuditModalVisible = ref(false); // 批量审核弹框显示状态
const batchAuditSubmitLoading = ref(false);
const batchAuditForm = ref({
auth_flag: 1,
reason: ''
});
const goodsGroupFlag = ref(false);
const goodsGroupLoading = ref(false);
const goodsGroupList = ref([]);
const goodsGroupForm = ref({
groupId: ""
});
const goodsGroupRule = reactive({
groupId: [{ required: true, message: "请选择商品分组", trigger: "change" }]
});
const goodsStatusWithCount = computed(() => {
return [
{title: '全部', value: 'ALL'},
{title: `出售中${goodsNumerData.value.upperGoodsNum ? '(' + goodsNumerData.value.upperGoodsNum + ')' : ''}`, value: 'UPPER'},
{title: `仓库中${goodsNumerData.value.downGoodsNum ? '(' + goodsNumerData.value.downGoodsNum + ')' : ''}`, value: 'DOWN'},
{title: `待审核${goodsNumerData.value.auditGoodsNum ? '(' + goodsNumerData.value.auditGoodsNum + ')' : ''}`, value: 'TOBEAUDITED'},
{title: `审核未通过${goodsNumerData.value.refuseGoodsNum ? '(' + goodsNumerData.value.refuseGoodsNum + ')' : ''}`, value: 'REFUSE'}
];
});
function clearTableSelection() {
table.value?.clearSelection?.();
}
function onStatusTabClick(tab) {
const status = tab.paneName ?? tab.props?.name;
goodsStatusClick(status);
}
function salesModelText(v) {
if (v === "RETAIL") return "零售";
if (v === "WHOLESALE") return "批发";
return "其他类型";
}
function goodsTypeText(v) {
if (v === "PHYSICAL_GOODS") return "实物商品";
if (v === "VIRTUAL_GOODS") return "虚拟商品";
if (v === "E_COUPON") return "电子卡券"; // 卡密商品
if (v === "APPOINTMENT_GOODS") return "预约商品";
return v || "—";
}
function marketEnableText(v) {
if (v === "DOWN") return "下架";
if (v === "UPPER") return "上架";
return "";
}
function authFlagText(v) {
if (v === "TOBEAUDITED") return "待审核";
if (v === "PASS") return "通过";
if (v === "REFUSE") return "拒绝";
return "";
}
// 初始化数据
function init() {
getDataList();
getNumberData();
loadGoodsGroupList();
}
// 分页 改变页码
function changePage(v) {
searchForm.pageNumber = v;
getDataList();
}
// 分页 改变页数
function changePageSize(v) {
searchForm.pageSize = v;
getDataList();
}
// 搜索
function handleSearch() {
searchForm.pageNumber = 1;
searchForm.pageSize = 20;
getDataList();
getNumberData();
}
// 获取数据
function getDataList() {
loading.value = true;
getGoodsListData(searchForm).then((res) => {
loading.value = false;
if (res.success) {
data.value = res.result.records;
total.value = res.result.total;
}
});
}
function getNumberData() {
// 创建一个不包含goodsStatus字段的搜索参数
const { goodsStatus, ...searchParams } = searchForm;
getGoodsNumerData(searchParams).then((res) => {
if (res.success) {
goodsNumerData.value = res.result;
}
})
}
// 编辑
function edit(v) {
id.value = v.id;
if (v.underMessage) {
underForm.reason = v.underMessage;
} else {
underForm.reason = "";
}
modalVisible.value = true;
}
// 下架
function lower() {
const reason = underForm.reason ? underForm.reason.trim() : "";
if (!reason) {
ElMessage.error("下架原因不能为空");
return;
}
let params = {
goodsId: id.value,
reason
};
lowGoods(params).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
modalVisible.value = false;
getDataList();
getNumberData(); // 添加这行
}
});
}
// 上架
function upper(v) {
ElMessageBox.confirm("您确认要上架 " + v.goodsName + " ?", "确认上架", { type: "warning" }).then(() => {
let params = {
goodsId: v.id
};
},
computed: {
goodsStatusWithCount() {
return [
{title: '全部', value: 'ALL'},
{title: `出售中${this.goodsNumerData.upperGoodsNum ? '(' + this.goodsNumerData.upperGoodsNum + ')' : ''}`, value: 'UPPER'},
{title: `仓库中${this.goodsNumerData.downGoodsNum ? '(' + this.goodsNumerData.downGoodsNum + ')' : ''}`, value: 'DOWN'},
{title: `待审核${this.goodsNumerData.auditGoodsNum ? '(' + this.goodsNumerData.auditGoodsNum + ')' : ''}`, value: 'TOBEAUDITED'},
{title: `审核未通过${this.goodsNumerData.refuseGoodsNum ? '(' + this.goodsNumerData.refuseGoodsNum + ')' : ''}`, value: 'REFUSE'}
];
return upGoods(params).then((res) => {
if (res.success) {
ElMessage.success("上架成功");
getDataList();
getNumberData(); // 添加这行
}
});
}).catch(() => {});
}
//查看商品详情
function showDetail(v) {
customRouterPush({
name: "goods-detail",
query: { id: v.id },
});
}
// 商品状态筛选
function goodsStatusClick(name) {
if (name === "ALL" || name === "" || name === undefined || name === null) {
delete searchForm.goodsStatus;
currentStatus.value = "ALL";
} else {
searchForm.goodsStatus = name;
currentStatus.value = name;
}
selectedRows.value = [];
clearTableSelection();
getDataList();
}
function examine(v, authFlag) {
// 审核商品
let examine = "通过";
goodsAuditForm.authFlag = "PASS";
if (authFlag != 1) {
examine = "拒绝";
goodsAuditForm.authFlag = "REFUSE";
}
ElMessageBox.confirm("您确认要审核" + examine + " " + v.goodsName + " ?", "确认审核", { type: "warning" }).then(() => {
goodsAuditForm.goodsIds=v.id;
let formData = new FormData();
formData.append('goodsIds', v.id);
formData.append('authFlag', goodsAuditForm.authFlag);
return authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success("审核成功");
getDataList();
getNumberData();
}
});
}).catch(() => {});
}
// 打开审核弹框
function openAuditModal(goods) {
currentAuditGoods.value = goods;
goodsAuditForm.auth_flag = 1;
goodsAuditForm.reason = '';
auditSubmitLoading.value = false;
auditModalVisible.value = true;
}
// 提交审核
function submitAudit() {
if (!currentAuditGoods.value) {
return;
}
auditSubmitLoading.value = true;
const formData = new FormData();
formData.append('goodsIds', currentAuditGoods.value.id);
formData.append('authFlag', goodsAuditForm.auth_flag === 1 ? 'PASS' : 'REFUSE');
authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success('审核成功');
auditModalVisible.value = false;
getDataList();
getNumberData();
}
},
methods: {
unitPrice,
clearTableSelection() {
this.$refs.table?.clearSelection?.();
},
onStatusTabClick(tab) {
const status = tab.paneName ?? tab.props?.name;
this.goodsStatusClick(status);
},
salesModelText(v) {
if (v === "RETAIL") return "零售";
if (v === "WHOLESALE") return "批发";
return "其他类型";
},
goodsTypeText(v) {
if (v === "PHYSICAL_GOODS") return "实物商品";
if (v === "VIRTUAL_GOODS") return "虚拟商品";
if (v === "E_COUPON") return "电子卡券"; // 卡密商品
if (v === "APPOINTMENT_GOODS") return "预约商品";
return v || "—";
},
marketEnableText(v) {
if (v === "DOWN") return "下架";
if (v === "UPPER") return "上架";
return "";
},
authFlagText(v) {
if (v === "TOBEAUDITED") return "待审核";
if (v === "PASS") return "通过";
if (v === "REFUSE") return "拒绝";
return "";
},
// 初始化数据
init() {
this.getDataList();
this.getNumberData();
this.loadGoodsGroupList();
},
// 分页 改变页码
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
// 分页 改变页数
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
// 搜索
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
this.getNumberData();
},
// 获取数据
getDataList() {
this.loading = true;
getGoodsListData(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
},
getNumberData() {
// 创建一个不包含goodsStatus字段的搜索参数
const { goodsStatus, ...searchParams } = this.searchForm;
getGoodsNumerData(searchParams).then((res) => {
if (res.success) {
this.goodsNumerData = res.result;
}
})
},
// 编辑
edit(v) {
this.id = v.id;
if (v.underMessage) {
this.underForm.reason = v.underMessage;
} else {
this.underForm.reason = "";
}).finally(() => {
auditSubmitLoading.value = false;
});
}
function onSelectionChange(selection) {
selectedRows.value = selection;
}
// 批量上架
function batchUpper() {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要上架的商品');
return;
}
const goodsNames = selectedRows.value.map(item => item.goodsName).join('、');
ElMessageBox.confirm(`您确认要上架以下商品吗?\n${goodsNames}`, "确认批量上架", { type: "warning" }).then(() => {
const goodsIds = selectedRows.value.map(item => item.id);
const params = {
goodsId: goodsIds
};
return upGoods(params).then((res) => {
if (res.success) {
ElMessage.success('批量上架成功');
selectedRows.value = [];
selectAll.value = false;
getDataList();
getNumberData();
}
this.modalVisible = true;
},
// 下架
lower() {
const reason = this.underForm.reason ? this.underForm.reason.trim() : "";
if (!reason) {
ElMessage.error("下架原因不能为空");
return;
});
}).catch(() => {});
}
// 批量下架
function batchLower() {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要下架的商品');
return;
}
const goodsNames = selectedRows.value.map(item => item.goodsName).join('、');
ElMessageBox.confirm(`您确认要下架以下商品吗?\n${goodsNames}`, "确认批量下架", { type: "warning" }).then(() => {
const goodsIds = selectedRows.value.map(item => item.id);
const params = {
goodsId: goodsIds,
reason: '批量下架操作'
};
return lowGoods(params).then((res) => {
if (res.success) {
ElMessage.success('批量下架成功');
selectedRows.value = [];
selectAll.value = false;
getDataList();
getNumberData();
}
let params = {
goodsId: this.id,
reason
};
lowGoods(params).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
this.modalVisible = false;
this.getDataList();
this.getNumberData(); // 添加这行
}
});
},
// 上架
upper(v) {
ElMessageBox.confirm("您确认要上架 " + v.goodsName + " ?", "确认上架", { type: "warning" }).then(() => {
let params = {
goodsId: v.id
};
return upGoods(params).then((res) => {
if (res.success) {
ElMessage.success("上架成功");
this.getDataList();
this.getNumberData(); // 添加这行
}
});
}).catch(() => {});
},
});
}).catch(() => {});
}
//查看商品详情
showDetail(v) {
customRouterPush({
name: "goods-detail",
query: { id: v.id },
});
},
// 批量审核
function batchAudit() {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要审核的商品');
return;
}
// 商品状态筛选
goodsStatusClick(name) {
if (name === "ALL" || name === "" || name === undefined || name === null) {
delete this.searchForm.goodsStatus;
this.currentStatus = "ALL";
} else {
this.searchForm.goodsStatus = name;
this.currentStatus = name;
}
this.selectedRows = [];
this.clearTableSelection();
this.getDataList();
},
examine(v, authFlag) {
// 审核商品
let examine = "通过";
this.goodsAuditForm.authFlag = "PASS";
if (authFlag != 1) {
examine = "拒绝";
this.goodsAuditForm.authFlag = "REFUSE";
}
// 重置批量审核表单
batchAuditForm.value = {
auth_flag: 1,
reason: ''
};
batchAuditModalVisible.value = true;
}
ElMessageBox.confirm("您确认要审核" + examine + " " + v.goodsName + " ?", "确认审核", { type: "warning" }).then(() => {
this.goodsAuditForm.goodsIds=v.id;
let formData = new FormData();
formData.append('goodsIds', v.id);
formData.append('authFlag', this.goodsAuditForm.authFlag);
// 提交批量审核
function submitBatchAudit() {
if (selectedRows.value.length === 0) {
ElMessage.warning('请先选择要审核的商品');
return;
}
return authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success("审核成功");
this.getDataList();
this.getNumberData();
}
});
}).catch(() => {});
},
// 如果是拒绝审核,必须填写原因
if (batchAuditForm.value.auth_flag === 2 && !batchAuditForm.value.reason.trim()) {
ElMessage.warning('审核拒绝时必须填写拒绝原因');
return;
}
// 打开审核弹框
openAuditModal(goods) {
this.currentAuditGoods = goods;
this.goodsAuditForm.auth_flag = 1;
this.goodsAuditForm.reason = '';
this.auditSubmitLoading = false;
this.auditModalVisible = true;
},
const actionText = batchAuditForm.value.auth_flag === 1 ? '通过' : '拒绝';
const goodsIds = selectedRows.value.map(item => item.id);
const formData = new FormData();
formData.append('goodsIds', goodsIds.join(','));
formData.append('authFlag', batchAuditForm.value.auth_flag === 1 ? 'PASS' : 'REFUSE');
if (batchAuditForm.value.reason) {
formData.append('reason', batchAuditForm.value.reason);
}
// 提交审核
submitAudit() {
if (!this.currentAuditGoods) {
return;
}
this.auditSubmitLoading = true;
const formData = new FormData();
formData.append('goodsIds', this.currentAuditGoods.id);
formData.append('authFlag', this.goodsAuditForm.auth_flag === 1 ? 'PASS' : 'REFUSE');
authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success('审核成功');
this.auditModalVisible = false;
this.getDataList();
this.getNumberData();
}
}).finally(() => {
this.auditSubmitLoading = false;
});
},
onSelectionChange(selection) {
this.selectedRows = selection;
},
// 批量上架
batchUpper() {
if (this.selectedRows.length === 0) {
ElMessage.warning('请先选择要上架的商品');
return;
}
const goodsNames = this.selectedRows.map(item => item.goodsName).join('、');
ElMessageBox.confirm(`您确认要上架以下商品吗?\n${goodsNames}`, "确认批量上架", { type: "warning" }).then(() => {
const goodsIds = this.selectedRows.map(item => item.id);
const params = {
goodsId: goodsIds
};
return upGoods(params).then((res) => {
if (res.success) {
ElMessage.success('批量上架成功');
this.selectedRows = [];
this.selectAll = false;
this.getDataList();
this.getNumberData();
}
});
}).catch(() => {});
},
// 批量下架
batchLower() {
if (this.selectedRows.length === 0) {
ElMessage.warning('请先选择要下架的商品');
return;
}
const goodsNames = this.selectedRows.map(item => item.goodsName).join('、');
ElMessageBox.confirm(`您确认要下架以下商品吗?\n${goodsNames}`, "确认批量下架", { type: "warning" }).then(() => {
const goodsIds = this.selectedRows.map(item => item.id);
const params = {
goodsId: goodsIds,
reason: '批量下架操作'
};
return lowGoods(params).then((res) => {
if (res.success) {
ElMessage.success('批量下架成功');
this.selectedRows = [];
this.selectAll = false;
this.getDataList();
this.getNumberData();
}
});
}).catch(() => {});
},
// 批量审核
batchAudit() {
if (this.selectedRows.length === 0) {
ElMessage.warning('请先选择要审核的商品');
return;
}
// 重置批量审核表单
this.batchAuditForm = {
auth_flag: 1,
reason: ''
};
this.batchAuditModalVisible = true;
},
// 提交批量审核
submitBatchAudit() {
if (this.selectedRows.length === 0) {
ElMessage.warning('请先选择要审核的商品');
return;
}
// 如果是拒绝审核,必须填写原因
if (this.batchAuditForm.auth_flag === 2 && !this.batchAuditForm.reason.trim()) {
ElMessage.warning('审核拒绝时必须填写拒绝原因');
return;
}
const actionText = this.batchAuditForm.auth_flag === 1 ? '通过' : '拒绝';
const goodsIds = this.selectedRows.map(item => item.id);
const formData = new FormData();
formData.append('goodsIds', goodsIds.join(','));
formData.append('authFlag', this.batchAuditForm.auth_flag === 1 ? 'PASS' : 'REFUSE');
if (this.batchAuditForm.reason) {
formData.append('reason', this.batchAuditForm.reason);
}
this.batchAuditSubmitLoading = true;
authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success(`批量审核${actionText}成功`);
this.selectedRows = [];
this.selectAll = false;
this.batchAuditModalVisible = false;
this.clearTableSelection();
this.getDataList();
this.getNumberData();
}
}).finally(() => {
this.batchAuditSubmitLoading = false;
});
},
loadGoodsGroupList() {
getGoodsGroupByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
if (res && res.success && res.result) {
this.goodsGroupList = res.result.records || [];
}
});
},
openSetGoodsGroup() {
if (this.selectedRows.length === 0) {
ElMessage.warning("请先选择商品");
return;
}
this.goodsGroupFlag = true;
this.goodsGroupLoading = false;
this.goodsGroupForm = { groupId: "" };
this.$nextTick(() => {
if (this.$refs.goodsGroupForm) this.$refs.goodsGroupForm.resetFields();
});
this.loadGoodsGroupList();
},
submitSetGoodsGroup() {
this.$refs.goodsGroupForm.validate((valid) => {
if (!valid) return;
const goodsIds = this.selectedRows.map((item) => item.id);
this.goodsGroupLoading = true;
addGoodsGroupItems(this.goodsGroupForm.groupId, goodsIds).then((res) => {
this.goodsGroupLoading = false;
if (res && res.success) {
ElMessage.success("设置成功");
this.goodsGroupFlag = false;
this.selectedRows = [];
this.selectAll = false;
this.clearTableSelection();
this.getDataList();
} else if (res && res.message) {
ElMessage.error(res.message);
}
}).catch(() => {
this.goodsGroupLoading = false;
ElMessage.error("设置失败,请检查权限或后端接口");
});
});
batchAuditSubmitLoading.value = true;
authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success(`批量审核${actionText}成功`);
selectedRows.value = [];
selectAll.value = false;
batchAuditModalVisible.value = false;
clearTableSelection();
getDataList();
getNumberData();
}
},
mounted() {
this.init();
},
};
}).finally(() => {
batchAuditSubmitLoading.value = false;
});
}
function loadGoodsGroupList() {
getGoodsGroupByPage({ pageNumber: 1, pageSize: 1000 }).then((res) => {
if (res && res.success && res.result) {
goodsGroupList.value = res.result.records || [];
}
});
}
function openSetGoodsGroup() {
if (selectedRows.value.length === 0) {
ElMessage.warning("请先选择商品");
return;
}
goodsGroupFlag.value = true;
goodsGroupLoading.value = false;
goodsGroupForm.value = { groupId: "" };
nextTick(() => {
if (goodsGroupFormRef.value) goodsGroupFormRef.value.resetFields();
});
loadGoodsGroupList();
}
function submitSetGoodsGroup() {
goodsGroupFormRef.value.validate((valid) => {
if (!valid) return;
const goodsIds = selectedRows.value.map((item) => item.id);
goodsGroupLoading.value = true;
addGoodsGroupItems(goodsGroupForm.value.groupId, goodsIds).then((res) => {
goodsGroupLoading.value = false;
if (res && res.success) {
ElMessage.success("设置成功");
goodsGroupFlag.value = false;
selectedRows.value = [];
selectAll.value = false;
clearTableSelection();
getDataList();
} else if (res && res.message) {
ElMessage.error(res.message);
}
}).catch(() => {
goodsGroupLoading.value = false;
ElMessage.error("设置失败,请检查权限或后端接口");
});
});
}
onMounted(() => {
init();
});
</script>
<style lang="scss" scoped>
// Tab组件样式

View File

@@ -2,7 +2,7 @@
<div class="search">
<el-card>
<el-form
ref="searchForm"
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
@@ -87,104 +87,103 @@
</div>
</template>
<script>
<script setup>
import { onMounted, ref } from "vue";
import { authGoods, getAuthGoodsListData } from "@/api/goods";
import { ElMessage, ElMessageBox } from "element-plus";
import { unitPrice, customRouterPush } from "@/utils/filters";
export default {
name: "goodsApply",
data() {
return {
loading: true,
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
},
goodsAuditForm: {
auth_flag: 1,
},
data: [],
total: 0,
};
},
methods: {
unitPrice,
authFlagText(v) {
const map = {
TOBEAUDITED: "待审核",
PASS: "审核通过",
REFUSE: "审核拒绝",
};
return map[v] || v || "-";
},
authFlagTagType(v) {
const map = {
TOBEAUDITED: "warning",
PASS: "success",
REFUSE: "danger",
};
return map[v] || "info";
},
init() {
this.getDataList();
},
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
changePageSize(v) {
this.searchForm.pageSize = v;
this.getDataList();
},
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
getDataList() {
this.loading = true;
this.searchForm.authFlag = 0;
getAuthGoodsListData(this.searchForm)
.then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
})
.catch(() => {
this.loading = false;
});
},
examine(v, authFlag) {
const examine = authFlag === 1 ? "通过" : "拒绝";
this.goodsAuditForm.authFlag = authFlag === 1 ? "PASS" : "REFUSE";
ElMessageBox.confirm(`您确认要审核${examine} ${v.goodsName} ?`, "确认审核", { type: "warning" }).then(() => {
const formData = new FormData();
formData.append("goodsIds", v.id);
formData.append("authFlag", this.goodsAuditForm.authFlag);
return authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success("审核成功");
this.getDataList();
}
});
}).catch(() => {});
},
showDetail(v) {
customRouterPush({
name: "goods-detail",
query: { id: v.id },
});
},
},
mounted() {
this.init();
},
};
defineOptions({ name: "goodsApply" });
// 模板中 el-form 的 ref 与数据 searchForm 同名,避免相互覆盖改名
const searchFormRef = ref(null);
const table = ref(null);
const loading = ref(true);
const searchForm = ref({
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
});
const goodsAuditForm = ref({
auth_flag: 1,
});
const data = ref([]);
const total = ref(0);
function authFlagText(v) {
const map = {
TOBEAUDITED: "待审核",
PASS: "审核通过",
REFUSE: "审核拒绝",
};
return map[v] || v || "-";
}
function authFlagTagType(v) {
const map = {
TOBEAUDITED: "warning",
PASS: "success",
REFUSE: "danger",
};
return map[v] || "info";
}
function init() {
getDataList();
}
function changePage(v) {
searchForm.value.pageNumber = v;
getDataList();
}
function changePageSize(v) {
searchForm.value.pageSize = v;
getDataList();
}
function handleSearch() {
searchForm.value.pageNumber = 1;
searchForm.value.pageSize = 20;
getDataList();
}
function getDataList() {
loading.value = true;
searchForm.value.authFlag = 0;
getAuthGoodsListData(searchForm.value)
.then((res) => {
loading.value = false;
if (res.success) {
data.value = res.result.records;
total.value = res.result.total;
}
})
.catch(() => {
loading.value = false;
});
}
function examine(v, authFlag) {
const examine = authFlag === 1 ? "通过" : "拒绝";
goodsAuditForm.value.authFlag = authFlag === 1 ? "PASS" : "REFUSE";
ElMessageBox.confirm(`您确认要审核${examine} ${v.goodsName} ?`, "确认审核", { type: "warning" }).then(() => {
const formData = new FormData();
formData.append("goodsIds", v.id);
formData.append("authFlag", goodsAuditForm.value.authFlag);
return authGoods(formData).then((res) => {
if (res.success) {
ElMessage.success("审核成功");
getDataList();
}
});
}).catch(() => {});
}
function showDetail(v) {
customRouterPush({
name: "goods-detail",
query: { id: v.id },
});
}
onMounted(() => {
init();
});
</script>
<style scoped>

View File

@@ -376,307 +376,304 @@
</div>
</template>
<script>
<script setup>
import { computed, nextTick, onMounted, reactive, ref } from "vue";
import { useRoute } from "vue-router";
import { View } from "@element-plus/icons-vue";
import { authGoods, getBrandListData, getGoodsDetail } from "@/api/goods";
import { ElMessage } from "element-plus";
export default {
name: "goodsDetail",
components: { View },
data() {
return {
loading: false,
goods: {},
brandList: [],
previewGoodsPicture: "",
goodsPictureVisible: false,
wholesaleData: [],
skuData: [],
activeTab: "basic",
slotCapacityVisible: false,
currentSlotCapacities: [],
goodsAuditForm: {
auth_flag: 1,
},
auditModalVisible: false,
auditSubmitLoading: false,
};
},
computed: {
isAppointmentGoods() {
return this.goods.goodsType === "APPOINTMENT_GOODS";
},
needsLogistics() {
return this.goods.goodsType === "PHYSICAL_GOODS";
},
salesModelDisplayText() {
const type = this.goods.goodsType;
const model = this.goods.salesModel;
if (type === "APPOINTMENT_GOODS") return "预约型";
if (type === "E_COUPON") return "电子卡券";
if (type === "VIRTUAL_GOODS") return "虚拟型";
if (model === "WHOLESALE") return "批发型";
if (model === "RETAIL") return "零售型";
return model || "—";
},
sectionTabs() {
const tabs = [
{ name: "basic", label: "基础信息" },
{ name: "media", label: "图文信息" },
];
if (this.isAppointmentGoods) {
tabs.push({ name: "appointment", label: "预约规则" });
}
tabs.push(
{ name: "price", label: "价格库存" },
{ name: "other", label: "其他信息" },
);
return tabs;
},
categoryText() {
const names = this.goods.categoryName;
if (!names || !names.length) return "—";
return names.join(" > ");
},
/** 兼容扁平列表与历史嵌套分组两种 params 结构 */
displayGoodsParams() {
let list = this.goods.goodsParamsDTOList;
if ((!Array.isArray(list) || !list.length) && this.goods.params) {
list = this.parseParamsJson(this.goods.params);
}
if (!Array.isArray(list) || !list.length) return [];
const flat = [];
list.forEach((item) => {
if (Array.isArray(item?.goodsParamsItemDTOList) && item.goodsParamsItemDTOList.length) {
item.goodsParamsItemDTOList.forEach((child) => {
if (child?.paramName) flat.push(child);
});
} else if (item?.paramName) {
flat.push(item);
}
defineOptions({ name: "goodsDetail" });
const route = useRoute();
const loading = ref(false);
const goods = ref({});
const brandList = ref([]);
const previewGoodsPicture = ref("");
const goodsPictureVisible = ref(false);
const wholesaleData = ref([]);
const skuData = ref([]);
const activeTab = ref("basic");
const slotCapacityVisible = ref(false);
const currentSlotCapacities = ref([]);
const goodsAuditForm = reactive({
auth_flag: 1,
});
const auditModalVisible = ref(false);
const auditSubmitLoading = ref(false);
const isAppointmentGoods = computed(() => {
return goods.value.goodsType === "APPOINTMENT_GOODS";
});
const needsLogistics = computed(() => {
return goods.value.goodsType === "PHYSICAL_GOODS";
});
const salesModelDisplayText = computed(() => {
const type = goods.value.goodsType;
const model = goods.value.salesModel;
if (type === "APPOINTMENT_GOODS") return "预约型";
if (type === "E_COUPON") return "电子卡券";
if (type === "VIRTUAL_GOODS") return "虚拟型";
if (model === "WHOLESALE") return "批发型";
if (model === "RETAIL") return "零售型";
return model || "—";
});
const sectionTabs = computed(() => {
const tabs = [
{ name: "basic", label: "基础信息" },
{ name: "media", label: "图文信息" },
];
if (isAppointmentGoods.value) {
tabs.push({ name: "appointment", label: "预约规则" });
}
tabs.push(
{ name: "price", label: "价格库存" },
{ name: "other", label: "其他信息" },
);
return tabs;
});
const categoryText = computed(() => {
const names = goods.value.categoryName;
if (!names || !names.length) return "—";
return names.join(" > ");
});
/** 兼容扁平列表与历史嵌套分组两种 params 结构 */
const displayGoodsParams = computed(() => {
let list = goods.value.goodsParamsDTOList;
if ((!Array.isArray(list) || !list.length) && goods.value.params) {
list = parseParamsJson(goods.value.params);
}
if (!Array.isArray(list) || !list.length) return [];
const flat = [];
list.forEach((item) => {
if (Array.isArray(item?.goodsParamsItemDTOList) && item.goodsParamsItemDTOList.length) {
item.goodsParamsItemDTOList.forEach((child) => {
if (child?.paramName) flat.push(child);
});
return flat;
},
appointmentSettings() {
return this.goods.appointmentSettings || null;
},
appointmentSlotTemplate() {
return this.goods.appointmentSlotTemplate || null;
},
appointmentSlots() {
return this.appointmentSlotTemplate?.slots || [];
},
bookableDateText() {
const s = this.appointmentSettings;
if (!s) return "—";
if (s.bookableDateMode === "CUSTOM") {
const parts = [];
if (s.bookableCustomStartDate && s.bookableCustomEndDate) {
parts.push(`${s.bookableCustomStartDate} ~ ${s.bookableCustomEndDate}`);
}
const weekdays = this.formatWeekdays(s.bookableWeekdays);
if (weekdays) parts.push(weekdays);
return parts.length ? parts.join(";") : "自定义";
} else if (item?.paramName) {
flat.push(item);
}
});
return flat;
});
const appointmentSettings = computed(() => {
return goods.value.appointmentSettings || null;
});
const appointmentSlotTemplate = computed(() => {
return goods.value.appointmentSlotTemplate || null;
});
const appointmentSlots = computed(() => {
return appointmentSlotTemplate.value?.slots || [];
});
const bookableDateText = computed(() => {
const s = appointmentSettings.value;
if (!s) return "—";
if (s.bookableDateMode === "CUSTOM") {
const parts = [];
if (s.bookableCustomStartDate && s.bookableCustomEndDate) {
parts.push(`${s.bookableCustomStartDate} ~ ${s.bookableCustomEndDate}`);
}
const weekdays = formatWeekdays(s.bookableWeekdays);
if (weekdays) parts.push(weekdays);
return parts.length ? parts.join(";") : "自定义";
}
if (s.bookableDateMode === "EVERY_DAY") return "每天";
return "—";
});
const bookableDaysText = computed(() => {
const s = appointmentSettings.value;
if (!s || s.bookableDays == null) return "—";
return `对用户展示 ${s.bookableDays} 天内的可预约日期`;
});
const advanceBookingText = computed(() => {
const s = appointmentSettings.value;
if (!s) return "—";
if (s.advanceMode === "NONE") return "无需提前预约";
if (s.advanceMode === "REQUIRE_HOURS" && s.advanceHours != null) {
return `需提前 ${s.advanceHours} 小时进行预约`;
}
return "—";
});
const cancelRuleText = computed(() => {
const s = appointmentSettings.value;
if (!s) return "—";
if (s.cancelMode === "NOT_ALLOWED") return "不允许取消";
if (s.cancelMode === "ALLOW_BEFORE_HOURS" && s.cancelAheadHours != null) {
return `服务开始 ${s.cancelAheadHours} 小时之前,允许取消并自动退款`;
}
return "—";
});
const formSubmitText = computed(() => {
const s = appointmentSettings.value;
if (!s) return "—";
if (s.formSubmitMode === "PER_APPOINTMENT") return "每个预约提交一次";
if (s.formSubmitMode === "PER_ORDER") return "每单提交一次";
return "—";
});
onMounted(() => {
loadBrandList();
initGoods(route.query.id);
});
function sectionNo(name) {
const index = sectionTabs.value.findIndex((tab) => tab.name === name);
return index >= 0 ? index + 1 : "";
}
function parseParamsJson(paramsJson) {
if (!paramsJson) return [];
try {
const array = JSON.parse(paramsJson);
if (!Array.isArray(array)) return [];
const items = [];
array.forEach((element) => {
if (Array.isArray(element?.goodsParamsItemDTOList)) {
items.push(...element.goodsParamsItemDTOList);
} else if (element?.paramName) {
items.push(element);
}
if (s.bookableDateMode === "EVERY_DAY") return "每天";
return "—";
},
bookableDaysText() {
const s = this.appointmentSettings;
if (!s || s.bookableDays == null) return "—";
return `对用户展示 ${s.bookableDays} 天内的可预约日期`;
},
advanceBookingText() {
const s = this.appointmentSettings;
if (!s) return "—";
if (s.advanceMode === "NONE") return "无需提前预约";
if (s.advanceMode === "REQUIRE_HOURS" && s.advanceHours != null) {
return `需提前 ${s.advanceHours} 小时进行预约`;
}
return "—";
},
cancelRuleText() {
const s = this.appointmentSettings;
if (!s) return "—";
if (s.cancelMode === "NOT_ALLOWED") return "不允许取消";
if (s.cancelMode === "ALLOW_BEFORE_HOURS" && s.cancelAheadHours != null) {
return `服务开始 ${s.cancelAheadHours} 小时之前,允许取消并自动退款`;
}
return "—";
},
formSubmitText() {
const s = this.appointmentSettings;
if (!s) return "—";
if (s.formSubmitMode === "PER_APPOINTMENT") return "每个预约提交一次";
if (s.formSubmitMode === "PER_ORDER") return "每单提交一次";
return "—";
},
},
mounted() {
this.loadBrandList();
this.initGoods(this.$route.query.id);
},
methods: {
sectionNo(name) {
const index = this.sectionTabs.findIndex((tab) => tab.name === name);
return index >= 0 ? index + 1 : "";
},
parseParamsJson(paramsJson) {
if (!paramsJson) return [];
try {
const array = JSON.parse(paramsJson);
if (!Array.isArray(array)) return [];
const items = [];
array.forEach((element) => {
if (Array.isArray(element?.goodsParamsItemDTOList)) {
items.push(...element.goodsParamsItemDTOList);
} else if (element?.paramName) {
items.push(element);
}
});
return items;
} catch {
return [];
}
},
jumpToTab(name) {
this.activeTab = name;
this.$nextTick(() => {
const el = document.getElementById(`goods-section-${name}`);
el?.scrollIntoView({ behavior: "smooth", block: "start" });
});
},
loadBrandList() {
getBrandListData().then((res) => {
this.brandList = Array.isArray(res) ? res : res?.result || [];
});
},
initGoods(id) {
if (!id) return;
this.loading = true;
getGoodsDetail(id)
.then((res) => {
if (!res?.success) return;
this.goods = res.result || {};
this.skuData = (res.result?.skuList || []).map((sku) => ({
id: sku.id,
specs: sku.simpleSpecs || sku.goodsName,
sn: sku.sn,
weight: sku.weight,
price: sku.price,
image: sku.goodsGalleryList,
quantity: sku.quantity,
slotCapacities: sku.slotCapacities,
}));
this.wholesaleData = res.result?.wholesaleList || [];
})
.finally(() => {
this.loading = false;
});
},
brandText(brandId) {
if (!brandId || brandId === "0") return "—";
const brand = this.brandList.find((item) => String(item.id) === String(brandId));
return brand?.name || brandId;
},
goodsTypeText(v) {
if (v === "PHYSICAL_GOODS") return "实物商品";
if (v === "VIRTUAL_GOODS") return "虚拟商品";
if (v === "E_COUPON") return "电子卡券";
if (v === "APPOINTMENT_GOODS") return "预约商品";
return v || "—";
},
normalizeSlotCapacities(caps) {
if (!Array.isArray(caps) || !caps.length) return [];
return caps.map((item) => ({
startTime: item.startTime || "",
endTime: item.endTime || "",
capacity: item.slotCapacity ?? item.capacity ?? 0,
});
return items;
} catch {
return [];
}
}
function jumpToTab(name) {
activeTab.value = name;
nextTick(() => {
const el = document.getElementById(`goods-section-${name}`);
el?.scrollIntoView({ behavior: "smooth", block: "start" });
});
}
function loadBrandList() {
getBrandListData().then((res) => {
brandList.value = Array.isArray(res) ? res : res?.result || [];
});
}
function initGoods(id) {
if (!id) return;
loading.value = true;
getGoodsDetail(id)
.then((res) => {
if (!res?.success) return;
goods.value = res.result || {};
skuData.value = (res.result?.skuList || []).map((sku) => ({
id: sku.id,
specs: sku.simpleSpecs || sku.goodsName,
sn: sku.sn,
weight: sku.weight,
price: sku.price,
image: sku.goodsGalleryList,
quantity: sku.quantity,
slotCapacities: sku.slotCapacities,
}));
},
hasSlotCapacities(row) {
return this.slotCapacityCount(row) > 0;
},
slotCapacityCount(row) {
return this.normalizeSlotCapacities(row?.slotCapacities).length;
},
openSlotCapacityDialog(row) {
this.currentSlotCapacities = this.normalizeSlotCapacities(row?.slotCapacities);
this.slotCapacityVisible = true;
},
formatSlotCapacity(capacity) {
const value = Number(capacity);
if (!value) return "不限制";
return String(value);
},
marketEnableText(v) {
if (v === "UPPER") return "上架";
if (v === "DOWN") return "下架";
return "—";
},
authFlagText(v) {
if (v === "TOBEAUDITED") return "待审核";
if (v === "PASS") return "通过";
if (v === "REFUSE") return "拒绝";
return v || "—";
},
authFlagTagType(v) {
if (v === "PASS") return "success";
if (v === "TOBEAUDITED") return "warning";
if (v === "REFUSE") return "danger";
return "info";
},
formatServiceModes(modes) {
if (!modes || !modes.length) return "—";
const map = {
IN_STORE: "到店服务",
ON_SITE: "上门服务",
};
return modes.map((m) => map[m] || m).join("、");
},
formatWeekdays(weekdays) {
if (!Array.isArray(weekdays) || !weekdays.length) return "";
const map = {
1: "周一",
2: "周二",
3: "周三",
4: "周四",
5: "周五",
6: "周六",
7: "周日",
};
return weekdays.map((d) => map[d] || d).join("、");
},
handleViewGoodsPicture(url) {
this.previewGoodsPicture = url;
this.goodsPictureVisible = true;
},
openAuditModal() {
this.goodsAuditForm.auth_flag = 1;
this.auditSubmitLoading = false;
this.auditModalVisible = true;
},
submitAudit() {
if (!this.goods.id) return;
this.auditSubmitLoading = true;
const formData = new FormData();
formData.append("goodsIds", this.goods.id);
formData.append("authFlag", this.goodsAuditForm.auth_flag === 1 ? "PASS" : "REFUSE");
authGoods(formData)
.then((res) => {
if (res.success) {
ElMessage.success("审核成功");
this.auditModalVisible = false;
this.initGoods(this.goods.id);
}
})
.finally(() => {
this.auditSubmitLoading = false;
});
},
},
};
wholesaleData.value = res.result?.wholesaleList || [];
})
.finally(() => {
loading.value = false;
});
}
function brandText(brandId) {
if (!brandId || brandId === "0") return "—";
const brand = brandList.value.find((item) => String(item.id) === String(brandId));
return brand?.name || brandId;
}
function goodsTypeText(v) {
if (v === "PHYSICAL_GOODS") return "实物商品";
if (v === "VIRTUAL_GOODS") return "虚拟商品";
if (v === "E_COUPON") return "电子卡券";
if (v === "APPOINTMENT_GOODS") return "预约商品";
return v || "—";
}
function normalizeSlotCapacities(caps) {
if (!Array.isArray(caps) || !caps.length) return [];
return caps.map((item) => ({
startTime: item.startTime || "",
endTime: item.endTime || "",
capacity: item.slotCapacity ?? item.capacity ?? 0,
}));
}
function hasSlotCapacities(row) {
return slotCapacityCount(row) > 0;
}
function slotCapacityCount(row) {
return normalizeSlotCapacities(row?.slotCapacities).length;
}
function openSlotCapacityDialog(row) {
currentSlotCapacities.value = normalizeSlotCapacities(row?.slotCapacities);
slotCapacityVisible.value = true;
}
function formatSlotCapacity(capacity) {
const value = Number(capacity);
if (!value) return "不限制";
return String(value);
}
function marketEnableText(v) {
if (v === "UPPER") return "上架";
if (v === "DOWN") return "下架";
return "—";
}
function authFlagText(v) {
if (v === "TOBEAUDITED") return "待审核";
if (v === "PASS") return "通过";
if (v === "REFUSE") return "拒绝";
return v || "—";
}
function authFlagTagType(v) {
if (v === "PASS") return "success";
if (v === "TOBEAUDITED") return "warning";
if (v === "REFUSE") return "danger";
return "info";
}
function formatServiceModes(modes) {
if (!modes || !modes.length) return "—";
const map = {
IN_STORE: "到店服务",
ON_SITE: "上门服务",
};
return modes.map((m) => map[m] || m).join("、");
}
function formatWeekdays(weekdays) {
if (!Array.isArray(weekdays) || !weekdays.length) return "";
const map = {
1: "周一",
2: "周二",
3: "周三",
4: "周四",
5: "周五",
6: "周六",
7: "周日",
};
return weekdays.map((d) => map[d] || d).join("、");
}
function handleViewGoodsPicture(url) {
previewGoodsPicture.value = url;
goodsPictureVisible.value = true;
}
function openAuditModal() {
goodsAuditForm.auth_flag = 1;
auditSubmitLoading.value = false;
auditModalVisible.value = true;
}
function submitAudit() {
if (!goods.value.id) return;
auditSubmitLoading.value = true;
const formData = new FormData();
formData.append("goodsIds", goods.value.id);
formData.append("authFlag", goodsAuditForm.auth_flag === 1 ? "PASS" : "REFUSE");
authGoods(formData)
.then((res) => {
if (res.success) {
ElMessage.success("审核成功");
auditModalVisible.value = false;
initGoods(goods.value.id);
}
})
.finally(() => {
auditSubmitLoading.value = false;
});
}
</script>
<style lang="scss" scoped>