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>

View File

@@ -2,7 +2,7 @@
<div class="search">
<el-card>
<el-form
ref="searchForm"
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
@@ -87,7 +87,7 @@
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="品牌名称" prop="name">
<el-input v-model="form.name" clearable style="width: 100%" />
</el-form-item>
@@ -110,7 +110,7 @@
<el-dialog v-model="picModelFlag" width="1200px" :show-close="true">
<ossManage
ref="ossManage"
ref="ossManageRef"
:is-component="true"
:initialize="picModelFlag"
@callback="callbackSelected"
@@ -146,13 +146,14 @@
</div>
</template>
<script>
<script setup>
import { ref, reactive, nextTick, onMounted, getCurrentInstance } from "vue";
import {
getManagerBrandPage,
addBrand,
updateBrand,
disableBrand,
delBrand,
delBrand as delBrandApi,
getCategoryTree,
getBrandCategoryListData,
saveBrandCategory,
@@ -162,222 +163,234 @@ import { regular } from "@/utils";
import { ElMessage, ElMessageBox } from "element-plus";
import defaultPic from "@/assets/default.png";
export default {
name: "brand",
components: {
ossManage,
},
data() {
return {
defaultPic,
loading: true,
modalType: 0,
modalVisible: false,
modalTitle: "",
picModelFlag: false,
categoryModalVisible: false,
categoryModalTitle: "关联分类",
categoryTreeLoading: false,
categoryTreeData: [],
categoryTreeKey: 0,
categorySubmitLoading: false,
currentBrandId: "",
selectedCategoryIds: [],
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
},
form: {
name: "",
logo: "",
deleteFlag: "",
},
formValidate: {
name: [regular.REQUIRED, regular.VARCHAR20],
logo: [regular.REQUIRED, regular.URL200],
},
submitLoading: false,
data: [],
total: 0,
};
},
methods: {
openLogoPicker() {
this.picModelFlag = true;
this.$nextTick(() => {
if (this.$refs.ossManage) {
this.$refs.ossManage.selectImage = true;
}
});
},
callbackSelected(val) {
this.picModelFlag = false;
this.form.logo = val.url;
},
buildCategoryTreeNodes(list) {
if (!Array.isArray(list) || list.length === 0) return [];
return list.map((item) => ({
id: item.id,
title: item.name,
children: this.buildCategoryTreeNodes(item.children || []),
}));
},
async openCategoryModal(row) {
this.currentBrandId = row.id;
this.categoryModalTitle = "关联分类 - " + (row.name || "");
this.categoryModalVisible = true;
this.categoryTreeLoading = true;
this.categoryTreeKey += 1;
this.categoryTreeData = [];
this.selectedCategoryIds = [];
try {
const [treeRes, bindRes] = await Promise.all([
getCategoryTree(),
getBrandCategoryListData(row.id),
]);
const selectedIds = Array.isArray(bindRes?.result)
? bindRes.result
.map((x) => (typeof x === "string" ? x : x && x.id))
.filter(Boolean)
: [];
this.selectedCategoryIds = selectedIds;
this.categoryTreeData =
treeRes && treeRes.success
? this.buildCategoryTreeNodes(treeRes.result || [])
: [];
} finally {
this.categoryTreeLoading = false;
defineOptions({ name: "brand" });
const { proxy } = getCurrentInstance();
const loading = ref(true);
const modalType = ref(0);
const modalVisible = ref(false);
const modalTitle = ref("");
const picModelFlag = ref(false);
const categoryModalVisible = ref(false);
const categoryModalTitle = ref("关联分类");
const categoryTreeLoading = ref(false);
const categoryTreeData = ref([]);
const categoryTreeKey = ref(0);
const categorySubmitLoading = ref(false);
const currentBrandId = ref("");
const selectedCategoryIds = ref([]);
const searchForm = reactive({
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
});
const form = ref({
name: "",
logo: "",
deleteFlag: "",
});
const formValidate = reactive({
name: [regular.REQUIRED, regular.VARCHAR20],
logo: [regular.REQUIRED, regular.URL200],
});
const submitLoading = ref(false);
const data = ref([]);
const total = ref(0);
const searchFormRef = ref(null);
const formRef = ref(null);
const ossManageRef = ref(null);
function openLogoPicker() {
picModelFlag.value = true;
nextTick(() => {
if (ossManageRef.value) {
ossManageRef.value.selectImage = true;
}
});
}
function callbackSelected(val) {
picModelFlag.value = false;
form.value.logo = val.url;
}
function buildCategoryTreeNodes(list) {
if (!Array.isArray(list) || list.length === 0) return [];
return list.map((item) => ({
id: item.id,
title: item.name,
children: buildCategoryTreeNodes(item.children || []),
}));
}
async function openCategoryModal(row) {
currentBrandId.value = row.id;
categoryModalTitle.value = "关联分类 - " + (row.name || "");
categoryModalVisible.value = true;
categoryTreeLoading.value = true;
categoryTreeKey.value += 1;
categoryTreeData.value = [];
selectedCategoryIds.value = [];
try {
const [treeRes, bindRes] = await Promise.all([
getCategoryTree(),
getBrandCategoryListData(row.id),
]);
const selectedIds = Array.isArray(bindRes?.result)
? bindRes.result
.map((x) => (typeof x === "string" ? x : x && x.id))
.filter(Boolean)
: [];
selectedCategoryIds.value = selectedIds;
categoryTreeData.value =
treeRes && treeRes.success
? buildCategoryTreeNodes(treeRes.result || [])
: [];
} finally {
categoryTreeLoading.value = false;
}
}
function onCategoryTreeCheckChange(_data, checkedInfo) {
selectedCategoryIds.value = checkedInfo?.checkedKeys || [];
}
function submitBrandCategory() {
if (!currentBrandId.value) return;
categorySubmitLoading.value = true;
saveBrandCategory(
currentBrandId.value,
(selectedCategoryIds.value || []).map((id) => String(id))
)
.then((res) => {
categorySubmitLoading.value = false;
if (res && res.success) {
ElMessage.success("操作成功");
categoryModalVisible.value = false;
}
},
onCategoryTreeCheckChange(_data, checkedInfo) {
this.selectedCategoryIds = checkedInfo?.checkedKeys || [];
},
submitBrandCategory() {
if (!this.currentBrandId) return;
this.categorySubmitLoading = true;
saveBrandCategory(
this.currentBrandId,
(this.selectedCategoryIds || []).map((id) => String(id))
)
.then((res) => {
this.categorySubmitLoading = false;
if (res && res.success) {
ElMessage.success("操作成功");
this.categoryModalVisible = false;
}
})
.catch(() => {
this.categorySubmitLoading = false;
});
},
handleStatusSwitchChange(row, checked) {
const disable = !checked;
ElMessageBox.confirm(
"您确认要" + (disable ? "禁用" : "启用") + "品牌 " + row.name + " ?",
disable ? "确认禁用" : "确认启用",
{ type: "warning" }
).then(() => {
return disableBrand(row.id, { disable }).then((res) => {
})
.catch(() => {
categorySubmitLoading.value = false;
});
}
function handleStatusSwitchChange(row, checked) {
const disable = !checked;
ElMessageBox.confirm(
"您确认要" + (disable ? "禁用" : "启用") + "品牌 " + row.name + " ?",
disable ? "确认禁用" : "确认启用",
{ type: "warning" }
).then(() => {
return disableBrand(row.id, { disable }).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
}
getDataList();
});
}).catch(() => {
nextTick(() => proxy.$forceUpdate());
});
}
function delBrand(row) {
ElMessageBox.confirm("您确认要删除 " + row.name + " ?", "确认删除", { type: "warning" }).then(() => {
return delBrandApi(row.id).then((res) => {
if (res.success) {
ElMessage.success("品牌删除成功!");
getDataList();
}
});
}).catch(() => {});
}
function init() {
getDataList();
}
function changePage(v) {
searchForm.pageNumber = v;
getDataList();
}
function changePageSize(v) {
searchForm.pageSize = v;
getDataList();
}
function handleSearch() {
searchForm.pageNumber = 1;
searchForm.pageSize = 20;
getDataList();
}
function getDataList() {
loading.value = true;
getManagerBrandPage(searchForm).then((res) => {
loading.value = false;
if (res.success) {
data.value = res.result.records;
total.value = res.result.total;
}
});
}
function handleSubmit() {
formRef.value.validate((valid) => {
if (valid) {
submitLoading.value = true;
if (modalType.value === 0) {
delete form.value.id;
addBrand(form.value).then((res) => {
submitLoading.value = false;
if (res.success) {
ElMessage.success("操作成功");
getDataList();
modalVisible.value = false;
}
this.getDataList();
});
}).catch(() => {
this.$nextTick(() => this.$forceUpdate());
});
},
delBrand(row) {
ElMessageBox.confirm("您确认要删除 " + row.name + " ?", "确认删除", { type: "warning" }).then(() => {
return delBrand(row.id).then((res) => {
} else {
updateBrand(form.value).then((res) => {
submitLoading.value = false;
if (res.success) {
ElMessage.success("品牌删除成功!");
this.getDataList();
ElMessage.success("操作成功");
getDataList();
modalVisible.value = false;
}
});
}).catch(() => {});
},
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;
getManagerBrandPage(this.searchForm).then((res) => {
this.loading = false;
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
});
},
handleSubmit() {
this.$refs.form.validate((valid) => {
if (valid) {
this.submitLoading = true;
if (this.modalType === 0) {
delete this.form.id;
addBrand(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
ElMessage.success("操作成功");
this.getDataList();
this.modalVisible = false;
}
});
} else {
updateBrand(this.form).then((res) => {
this.submitLoading = false;
if (res.success) {
ElMessage.success("操作成功");
this.getDataList();
this.modalVisible = false;
}
});
}
}
});
},
add() {
this.modalType = 0;
this.modalTitle = "添加";
this.$refs.form?.resetFields();
delete this.form.id;
this.modalVisible = true;
},
edit(v) {
this.modalType = 1;
this.modalTitle = "编辑";
this.$refs.form?.resetFields();
for (const attr in v) {
if (v[attr] === null) {
v[attr] = "";
}
}
const str = JSON.stringify(v);
const data = JSON.parse(str);
this.form = data;
this.modalVisible = true;
},
},
mounted() {
this.init();
},
};
}
});
}
function add() {
modalType.value = 0;
modalTitle.value = "添加";
formRef.value?.resetFields();
delete form.value.id;
modalVisible.value = true;
}
function edit(v) {
modalType.value = 1;
modalTitle.value = "编辑";
formRef.value?.resetFields();
for (const attr in v) {
if (v[attr] === null) {
v[attr] = "";
}
}
const str = JSON.stringify(v);
const data = JSON.parse(str);
form.value = data;
modalVisible.value = true;
}
onMounted(() => {
init();
});
</script>
<style lang="scss" scoped>

View File

@@ -62,7 +62,7 @@
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="form" :model="formAdd" label-width="110px" :rules="formValidate">
<el-form ref="formRef" :model="formAdd" label-width="110px" :rules="formValidate">
<el-form-item v-if="showParent" label="上级分类" prop="parentId">
{{ parentTitle }}
<el-input v-model="formAdd.parentId" style="display: none" />
@@ -106,7 +106,7 @@
width="500px"
:close-on-click-modal="false"
>
<el-form ref="brandForm" :model="brandForm" label-width="100px">
<el-form ref="brandFormRef" :model="brandForm" label-width="100px">
<el-select v-model="brandForm.categoryBrands" filterable multiple style="width: 100%">
<el-option v-for="item in brandWay" :key="item.id" :label="item.name" :value="item.id" />
</el-select>
@@ -120,7 +120,8 @@
</div>
</template>
<script>
<script setup>
import { ref, reactive, onMounted } from "vue";
import {
delCategory,
disableCategory,
@@ -128,241 +129,249 @@ import {
getCategoryBrandListData,
getCategoryTree,
insertCategory,
saveCategoryBrand,
saveCategoryBrand as apiSaveCategoryBrand,
updateCategory,
} from "@/api/goods";
import uploadPicInput from "@/components/lili/upload-pic-input";
import { regular } from "@/utils";
import { ElMessage, ElMessageBox } from "element-plus";
export default {
name: "goods-category",
components: {
uploadPicInput,
},
data() {
return {
submitLoading: false,
categoryList: [],
loading: false,
brands: [],
categoryId: "",
modalType: 0,
modalVisible: false,
modalBrandVisible: false,
modalTitle: "",
showParent: false,
parentTitle: "",
modalBrandTitle: "",
formAdd: {
parentId: "",
name: "",
image: "",
sortOrder: 0,
deleteFlag: false,
commissionRate: 0,
level: 0,
},
brandForm: {
categoryBrands: [],
},
brandWay: [],
formValidate: {
commissionRate: [regular.REQUIRED, regular.INTEGER],
name: [regular.REQUIRED, regular.VARCHAR20],
sortOrder: [regular.REQUIRED, regular.INTEGER],
},
tableData: [],
};
},
methods: {
normalizeCategoryTree(list) {
if (!Array.isArray(list) || list.length === 0) return;
list.forEach((item) => {
if (!item || typeof item !== "object") return;
if (item.deleteFlag === 0) item.deleteFlag = false;
else if (item.deleteFlag === 1) item.deleteFlag = true;
else item.deleteFlag = !!item.deleteFlag;
if (Array.isArray(item.children) && item.children.length) {
this.normalizeCategoryTree(item.children);
}
});
},
onStatusSwitchChange(row, nextDeleteFlag) {
const previousDeleteFlag = !nextDeleteFlag;
const isClosing = nextDeleteFlag === true;
ElMessageBox.confirm(
`您是否要${isClosing ? "关闭" : "开启"}当前分类 ${row.name} 及其子分类?`,
isClosing ? "确认关闭" : "确认开启",
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
).then(() => {
row._statusLoading = true;
return disableCategory(row.id, { enableOperations: isClosing ? true : 0 }).then((res) => {
row._statusLoading = false;
if (res && res.success) {
ElMessage.success("操作成功");
this.getAllList();
return;
}
row.deleteFlag = previousDeleteFlag;
});
}).catch(() => {
row.deleteFlag = previousDeleteFlag;
});
},
init() {
this.getAllList();
this.getBrandList();
},
getBrandList() {
getBrandListData().then((res) => {
this.brandWay = res;
});
},
brandOperation(v) {
getCategoryBrandListData(v.id).then((res) => {
this.categoryId = v.id;
this.modalBrandTitle = "品牌关联";
this.brandForm.categoryBrands = res.result.map((item) => item.id);
this.modalBrandVisible = true;
});
},
saveCategoryBrand() {
saveCategoryBrand(this.categoryId, this.brandForm).then((res) => {
this.submitLoading = false;
defineOptions({ name: "goods-category" });
const submitLoading = ref(false);
const categoryList = ref([]);
const loading = ref(false);
const brands = ref([]);
const categoryId = ref("");
const modalType = ref(0);
const modalVisible = ref(false);
const modalBrandVisible = ref(false);
const modalTitle = ref("");
const showParent = ref(false);
const parentTitle = ref("");
const modalBrandTitle = ref("");
const formAdd = reactive({
parentId: "",
name: "",
image: "",
sortOrder: 0,
deleteFlag: false,
commissionRate: 0,
level: 0,
});
const brandForm = reactive({
categoryBrands: [],
});
const brandWay = ref([]);
const formValidate = reactive({
commissionRate: [regular.REQUIRED, regular.INTEGER],
name: [regular.REQUIRED, regular.VARCHAR20],
sortOrder: [regular.REQUIRED, regular.INTEGER],
});
const tableData = ref([]);
const formRef = ref(null);
const brandFormRef = ref(null);
function normalizeCategoryTree(list) {
if (!Array.isArray(list) || list.length === 0) return;
list.forEach((item) => {
if (!item || typeof item !== "object") return;
if (item.deleteFlag === 0) item.deleteFlag = false;
else if (item.deleteFlag === 1) item.deleteFlag = true;
else item.deleteFlag = !!item.deleteFlag;
if (Array.isArray(item.children) && item.children.length) {
normalizeCategoryTree(item.children);
}
});
}
function onStatusSwitchChange(row, nextDeleteFlag) {
const previousDeleteFlag = !nextDeleteFlag;
const isClosing = nextDeleteFlag === true;
ElMessageBox.confirm(
`您是否要${isClosing ? "关闭" : "开启"}当前分类 ${row.name} 及其子分类?`,
isClosing ? "确认关闭" : "确认开启",
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
).then(() => {
row._statusLoading = true;
return disableCategory(row.id, { enableOperations: isClosing ? true : 0 }).then((res) => {
row._statusLoading = false;
if (res && res.success) {
ElMessage.success("操作成功");
getAllList();
return;
}
row.deleteFlag = previousDeleteFlag;
});
}).catch(() => {
row.deleteFlag = previousDeleteFlag;
});
}
function init() {
getAllList();
getBrandList();
}
function getBrandList() {
getBrandListData().then((res) => {
brandWay.value = res;
});
}
function brandOperation(v) {
getCategoryBrandListData(v.id).then((res) => {
categoryId.value = v.id;
modalBrandTitle.value = "品牌关联";
brandForm.categoryBrands = res.result.map((item) => item.id);
modalBrandVisible.value = true;
});
}
function saveCategoryBrand() {
apiSaveCategoryBrand(categoryId.value, brandForm).then((res) => {
submitLoading.value = false;
if (res.success) {
ElMessage.success("操作成功");
modalBrandVisible.value = false;
}
});
}
function addChildren(v) {
modalType.value = 0;
modalTitle.value = "添加子分类";
parentTitle.value = v.name;
formAdd.level = Number(v.level) + 1;
formAdd.commissionRate = v.commissionRate;
showParent.value = true;
delete formAdd.id;
formAdd.parentId = v.id;
modalVisible.value = true;
}
function edit(v) {
modalType.value = 1;
modalTitle.value = "编辑";
formAdd.id = v.id;
formAdd.name = v.name;
formAdd.level = v.level;
formAdd.parentId = v.parentId;
formAdd.sortOrder = v.sortOrder;
formAdd.commissionRate = v.commissionRate;
formAdd.deleteFlag = v.deleteFlag;
formAdd.image = v.image;
showParent.value = false;
modalVisible.value = true;
}
function addParent() {
modalType.value = 0;
modalTitle.value = "添加一级分类";
parentTitle.value = "顶级分类";
showParent.value = true;
formRef.value?.resetFields();
delete formAdd.id;
formAdd.parentId = 0;
formAdd.level = 0;
modalVisible.value = true;
}
function Submit() {
formRef.value.validate((valid) => {
if (!valid) return;
submitLoading.value = true;
if (modalType.value === 0) {
delete formAdd.id;
insertCategory(formAdd).then((res) => {
submitLoading.value = false;
if (res.success) {
ElMessage.success("操作成功");
this.modalBrandVisible = false;
ElMessage.success("添加成功");
getAllList();
modalVisible.value = false;
formRef.value.resetFields();
}
});
},
addChildren(v) {
this.modalType = 0;
this.modalTitle = "添加子分类";
this.parentTitle = v.name;
this.formAdd.level = Number(v.level) + 1;
this.formAdd.commissionRate = v.commissionRate;
this.showParent = true;
delete this.formAdd.id;
this.formAdd.parentId = v.id;
this.modalVisible = true;
},
edit(v) {
this.modalType = 1;
this.modalTitle = "编辑";
this.formAdd.id = v.id;
this.formAdd.name = v.name;
this.formAdd.level = v.level;
this.formAdd.parentId = v.parentId;
this.formAdd.sortOrder = v.sortOrder;
this.formAdd.commissionRate = v.commissionRate;
this.formAdd.deleteFlag = v.deleteFlag;
this.formAdd.image = v.image;
this.showParent = false;
this.modalVisible = true;
},
addParent() {
this.modalType = 0;
this.modalTitle = "添加一级分类";
this.parentTitle = "顶级分类";
this.showParent = true;
this.$refs.form?.resetFields();
delete this.formAdd.id;
this.formAdd.parentId = 0;
this.formAdd.level = 0;
this.modalVisible = true;
},
Submit() {
this.$refs.form.validate((valid) => {
if (!valid) return;
this.submitLoading = true;
if (this.modalType === 0) {
delete this.formAdd.id;
insertCategory(this.formAdd).then((res) => {
this.submitLoading = false;
if (res.success) {
ElMessage.success("添加成功");
this.getAllList();
this.modalVisible = false;
this.$refs.form.resetFields();
}
});
} else {
updateCategory(this.formAdd).then((res) => {
this.submitLoading = false;
if (res.success) {
ElMessage.success("修改成功");
this.getAllList();
this.modalVisible = false;
this.$refs.form.resetFields();
}
});
} else {
updateCategory(formAdd).then((res) => {
submitLoading.value = false;
if (res.success) {
ElMessage.success("修改成功");
getAllList();
modalVisible.value = false;
formRef.value.resetFields();
}
});
},
remove(v) {
ElMessageBox.confirm("您确认要删除 " + v.name + " ?", "确认删除", { type: "warning" }).then(() => {
return delCategory(v.id).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
this.getAllList();
}
});
}).catch(() => {});
},
getAllList() {
this.loading = true;
getCategoryTree()
.then((res) => {
this.loading = false;
if (res.success) {
localStorage.setItem("category", JSON.stringify(res.result));
this.normalizeCategoryTree(res.result);
this.categoryList = JSON.parse(JSON.stringify(res.result));
this.tableData = JSON.parse(JSON.stringify(res.result));
}
})
.catch(() => {
this.loading = false;
});
},
enable(v) {
ElMessageBox.confirm(
"您是否要启用当前分类 " + v.name + " 及其子分类?",
"确认启用",
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
).then(() => {
return disableCategory(v.id, { enableOperations: 0 }).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
this.getAllList();
}
});
}).catch(() => {
this.getAllList();
});
},
disable(v) {
ElMessageBox.confirm(
"您是否要禁用当前分类 " + v.name + " 及其子分类?",
"确认禁用",
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
).then(() => {
return disableCategory(v.id, { enableOperations: true }).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
this.getAllList();
}
});
}).catch(() => {
this.getAllList();
});
},
},
mounted() {
this.init();
},
};
}
});
}
function remove(v) {
ElMessageBox.confirm("您确认要删除 " + v.name + " ?", "确认删除", { type: "warning" }).then(() => {
return delCategory(v.id).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
getAllList();
}
});
}).catch(() => {});
}
function getAllList() {
loading.value = true;
getCategoryTree()
.then((res) => {
loading.value = false;
if (res.success) {
localStorage.setItem("category", JSON.stringify(res.result));
normalizeCategoryTree(res.result);
categoryList.value = JSON.parse(JSON.stringify(res.result));
tableData.value = JSON.parse(JSON.stringify(res.result));
}
})
.catch(() => {
loading.value = false;
});
}
function enable(v) {
ElMessageBox.confirm(
"您是否要启用当前分类 " + v.name + " 及其子分类?",
"确认启用",
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
).then(() => {
return disableCategory(v.id, { enableOperations: 0 }).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
getAllList();
}
});
}).catch(() => {
getAllList();
});
}
function disable(v) {
ElMessageBox.confirm(
"您是否要禁用当前分类 " + v.name + " 及其子分类?",
"确认禁用",
{ type: "warning", confirmButtonText: "是", cancelButtonText: "否" }
).then(() => {
return disableCategory(v.id, { enableOperations: true }).then((res) => {
if (res.success) {
ElMessage.success("操作成功");
getAllList();
}
});
}).catch(() => {
getAllList();
});
}
onMounted(() => {
init();
});
</script>
<style lang="scss" scoped>

View File

@@ -1,7 +1,7 @@
<template>
<div class="search">
<el-card>
<el-form ref="form" :model="form" label-width="100px" :rules="formValidate">
<el-form ref="formRef" :model="form" label-width="100px" :rules="formValidate">
<el-form-item label="参数名称" prop="paramName">
<el-input v-model="form.paramName" clearable style="width: 520px" />
</el-form-item>
@@ -92,7 +92,9 @@
</div>
</template>
<script>
<script setup>
import { computed, nextTick, onMounted, reactive, ref, watch } from "vue";
import { useRoute, useRouter } from "vue-router";
import {
getCategoryTree,
getGoodsParamsDetail,
@@ -102,6 +104,11 @@ import {
import { regular } from "@/utils";
import { ElMessage } from "element-plus";
defineOptions({ name: "parameterEdit" });
const route = useRoute();
const router = useRouter();
const getOptionText = (value) => {
if (value && typeof value === "object") return value.value;
return value;
@@ -213,228 +220,228 @@ const buildFormState = (source, parameterId) => {
return formState;
};
export default {
name: "parameterEdit",
data() {
return {
submitLoading: false,
modalType: 0,
categoryModalVisible: false,
categoryModalTitle: "关联分类",
categoryTreeLoading: false,
categoryTreeData: [],
categoryTreeSource: [],
categoryIdNameMap: {},
categoryTreeKey: 0,
selectedCategoryIds: [],
form: {
paramName: "",
options: [{ value: "" }],
required: 0,
isIndex: 0,
sort: 0,
categoryIds: [],
},
formValidate: {
paramName: [regular.REQUIRED, regular.VARCHAR5],
options: [{ required: true, validator: validateOptions, trigger: "change" }],
required: [{ required: true, validator: validateRadioRequired("请选择是否必填"), trigger: "change" }],
isIndex: [{ required: true, validator: validateRadioRequired("请选择是否索引"), trigger: "change" }],
sort: [regular.REQUIRED, regular.INTEGER],
categoryIds: [
{
type: "array",
required: true,
min: 1,
message: "请选择关联分类",
trigger: "change",
},
],
},
};
},
computed: {
id() {
return this.$route.query && this.$route.query.id ? String(this.$route.query.id) : "";
const formRef = ref(null);
const categoryTree = ref(null);
const submitLoading = ref(false);
const modalType = ref(0);
const categoryModalVisible = ref(false);
const categoryModalTitle = ref("关联分类");
const categoryTreeLoading = ref(false);
const categoryTreeData = ref([]);
const categoryTreeSource = ref([]);
const categoryIdNameMap = ref({});
const categoryTreeKey = ref(0);
const selectedCategoryIds = ref([]);
const form = ref({
paramName: "",
options: [{ value: "" }],
required: 0,
isIndex: 0,
sort: 0,
categoryIds: [],
});
const formValidate = reactive({
paramName: [regular.REQUIRED, regular.VARCHAR5],
options: [{ required: true, validator: validateOptions, trigger: "change" }],
required: [{ required: true, validator: validateRadioRequired("请选择是否必填"), trigger: "change" }],
isIndex: [{ required: true, validator: validateRadioRequired("请选择是否索引"), trigger: "change" }],
sort: [regular.REQUIRED, regular.INTEGER],
categoryIds: [
{
type: "array",
required: true,
min: 1,
message: "请选择关联分类",
trigger: "change",
},
selectedCategoryNamesText() {
if (!Array.isArray(this.categoryTreeSource) || this.categoryTreeSource.length === 0) return "";
const selectedSet = new Set(toStringArray(this.selectedCategoryIds));
const isSelected = (node) => {
if (!node) return false;
const nodeId = node.id !== undefined && node.id !== null ? String(node.id) : "";
if (nodeId && selectedSet.has(nodeId)) return true;
const children = Array.isArray(node.children) ? node.children : [];
if (children.length === 0) return false;
return children.every(isSelected);
};
const collect = (node, out) => {
if (!node) return;
if (isSelected(node)) {
if (node.name) out.push(node.name);
return;
}
const children = Array.isArray(node.children) ? node.children : [];
children.forEach((c) => collect(c, out));
};
const names = [];
this.categoryTreeSource.forEach((n) => collect(n, names));
return names.join(",");
},
},
watch: {
"form.required"(val) {
const n = normalize01(val, 0);
if (val !== n) this.form.required = n;
},
"form.isIndex"(val) {
const n = normalize01(val, 0);
if (val !== n) this.form.isIndex = n;
},
},
methods: {
back() {
this.$router.push({ name: "goods-parameter" });
},
syncCategoryIds(ids) {
const categoryIds = toStringArray(ids);
this.selectedCategoryIds = categoryIds;
if (!this.form) this.form = {};
this.form.categoryIds = categoryIds;
},
touchOptionsValidate() {
this.$nextTick(() => {
const form = this.$refs.form;
if (form && typeof form.validateField === "function") {
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
form.validateField("options", () => {});
}
});
},
addOptionRow() {
if (!Array.isArray(this.form.options)) this.form.options = [];
this.form.options.push({ value: "" });
},
removeOptionRow(index) {
if (!Array.isArray(this.form.options)) this.form.options = [];
this.form.options.splice(index, 1);
this.touchOptionsValidate();
},
buildCategoryTreeNodes(list) {
if (!Array.isArray(list) || list.length === 0) return [];
return list.map((item) => ({
id: item.id,
title: item.name,
children: this.buildCategoryTreeNodes(item.children || []),
}));
},
async loadDetail(parameterId) {
if (!parameterId) return;
const res = await getGoodsParamsDetail(parameterId).catch(() => null);
if (!(res && res.success && res.result)) return;
const dto = res.result;
this.form = buildFormState(dto, parameterId);
this.selectedCategoryIds = [...this.form.categoryIds];
},
async openCategoryModal() {
this.categoryModalVisible = true;
this.categoryTreeLoading = true;
this.categoryTreeKey += 1;
],
});
const id = computed(() =>
route.query && route.query.id ? String(route.query.id) : ""
);
const selectedCategoryNamesText = computed(() => {
if (!Array.isArray(categoryTreeSource.value) || categoryTreeSource.value.length === 0) return "";
const selectedSet = new Set(toStringArray(selectedCategoryIds.value));
const isSelected = (node) => {
if (!node) return false;
const nodeId = node.id !== undefined && node.id !== null ? String(node.id) : "";
if (nodeId && selectedSet.has(nodeId)) return true;
const children = Array.isArray(node.children) ? node.children : [];
if (children.length === 0) return false;
return children.every(isSelected);
};
const collect = (node, out) => {
if (!node) return;
if (isSelected(node)) {
if (node.name) out.push(node.name);
return;
}
const children = Array.isArray(node.children) ? node.children : [];
children.forEach((c) => collect(c, out));
};
const names = [];
categoryTreeSource.value.forEach((n) => collect(n, names));
return names.join(",");
});
watch(
() => form.value.required,
(val) => {
const n = normalize01(val, 0);
if (val !== n) form.value.required = n;
}
);
watch(
() => form.value.isIndex,
(val) => {
const n = normalize01(val, 0);
if (val !== n) form.value.isIndex = n;
}
);
function back() {
router.push({ name: "goods-parameter" });
}
function syncCategoryIds(ids) {
const categoryIds = toStringArray(ids);
selectedCategoryIds.value = categoryIds;
if (!form.value) form.value = {};
form.value.categoryIds = categoryIds;
}
function touchOptionsValidate() {
nextTick(() => {
const formEl = formRef.value;
if (formEl && typeof formEl.validateField === "function") {
// validateField rejects on failure; catch to avoid dev-server [object Object] overlay
formEl.validateField("options", () => {});
}
});
}
function addOptionRow() {
if (!Array.isArray(form.value.options)) form.value.options = [];
form.value.options.push({ value: "" });
}
function removeOptionRow(index) {
if (!Array.isArray(form.value.options)) form.value.options = [];
form.value.options.splice(index, 1);
touchOptionsValidate();
}
function buildCategoryTreeNodes(list) {
if (!Array.isArray(list) || list.length === 0) return [];
return list.map((item) => ({
id: item.id,
title: item.name,
children: buildCategoryTreeNodes(item.children || []),
}));
}
async function loadDetail(parameterId) {
if (!parameterId) return;
const res = await getGoodsParamsDetail(parameterId).catch(() => null);
if (!(res && res.success && res.result)) return;
const dto = res.result;
form.value = buildFormState(dto, parameterId);
selectedCategoryIds.value = [...form.value.categoryIds];
}
async function openCategoryModal() {
categoryModalVisible.value = true;
categoryTreeLoading.value = true;
categoryTreeKey.value += 1;
try {
if (!Array.isArray(categoryTreeSource.value) || categoryTreeSource.value.length === 0) {
const treeRes = await getCategoryTree();
categoryTreeSource.value = treeRes && treeRes.success ? treeRes.result || [] : [];
const map = {};
buildCategoryIdNameMap(categoryTreeSource.value, map);
categoryIdNameMap.value = map;
}
categoryTreeData.value = buildCategoryTreeNodes(categoryTreeSource.value || []);
} finally {
categoryTreeLoading.value = false;
}
}
function onCategoryTreeCheckChange(_data, checkedInfo) {
syncCategoryIds(checkedInfo?.checkedKeys || []);
nextTick(() => {
const formEl = formRef.value;
if (formEl && typeof formEl.validateField === "function") {
formEl.validateField("categoryIds", () => {});
}
});
}
function initForm() {
if (id.value) {
modalType.value = 1;
const cached = window.sessionStorage.getItem(cacheKey(id.value));
if (cached) {
try {
if (!Array.isArray(this.categoryTreeSource) || this.categoryTreeSource.length === 0) {
const treeRes = await getCategoryTree();
this.categoryTreeSource = treeRes && treeRes.success ? treeRes.result || [] : [];
const map = {};
buildCategoryIdNameMap(this.categoryTreeSource, map);
this.categoryIdNameMap = map;
}
this.categoryTreeData = this.buildCategoryTreeNodes(this.categoryTreeSource || []);
} finally {
this.categoryTreeLoading = false;
const row = JSON.parse(cached);
form.value = buildFormState(row, row.id);
selectedCategoryIds.value = [...form.value.categoryIds];
} catch (e) {
modalType.value = 0;
}
},
onCategoryTreeCheckChange(_data, checkedInfo) {
this.syncCategoryIds(checkedInfo?.checkedKeys || []);
this.$nextTick(() => {
const form = this.$refs.form;
if (form && typeof form.validateField === "function") {
form.validateField("categoryIds", () => {});
}
});
},
initForm() {
if (this.id) {
this.modalType = 1;
const cached = window.sessionStorage.getItem(cacheKey(this.id));
if (cached) {
try {
const row = JSON.parse(cached);
this.form = buildFormState(row, row.id);
this.selectedCategoryIds = [...this.form.categoryIds];
} catch (e) {
this.modalType = 0;
}
}
this.loadDetail(String(this.id));
} else {
this.modalType = 0;
if (!Array.isArray(this.form.options) || this.form.options.length === 0) {
this.form.options = [{ value: "" }];
}
this.syncCategoryIds([]);
}
},
handleSubmit() {
this.$refs.form.validate((valid) => {
if (!valid) return;
this.submitLoading = true;
const options = normalizeOptions(this.form.options);
const categoryIds = toStringArray(this.form.categoryIds);
const payload = {
...this.form,
options: options.join(","),
required: Number(this.form.required),
isIndex: Number(this.form.isIndex),
sort: Number(this.form.sort || 0),
categoryParameterList: categoryIds.map((categoryId) => ({ categoryId })),
};
if (this.modalType === 0) {
delete payload.id;
insertGoodsParams(buildSpringFormPayload(payload))
.then((res) => {
if (!(res && res.success)) return;
ElMessage.success("操作成功");
this.back();
})
.finally(() => {
this.submitLoading = false;
});
} else {
updateGoodsParams(buildSpringFormPayload(payload))
.then((res) => {
if (!(res && res.success)) return;
ElMessage.success("操作成功");
this.back();
})
.finally(() => {
this.submitLoading = false;
});
}
});
},
},
mounted() {
getCategoryTree().then((res) => {
if (res && res.success) {
this.categoryTreeSource = res.result || [];
const map = {};
buildCategoryIdNameMap(this.categoryTreeSource, map);
this.categoryIdNameMap = map;
}
});
this.initForm();
},
};
}
loadDetail(String(id.value));
} else {
modalType.value = 0;
if (!Array.isArray(form.value.options) || form.value.options.length === 0) {
form.value.options = [{ value: "" }];
}
syncCategoryIds([]);
}
}
function handleSubmit() {
formRef.value.validate((valid) => {
if (!valid) return;
submitLoading.value = true;
const options = normalizeOptions(form.value.options);
const categoryIds = toStringArray(form.value.categoryIds);
const payload = {
...form.value,
options: options.join(","),
required: Number(form.value.required),
isIndex: Number(form.value.isIndex),
sort: Number(form.value.sort || 0),
categoryParameterList: categoryIds.map((categoryId) => ({ categoryId })),
};
if (modalType.value === 0) {
delete payload.id;
insertGoodsParams(buildSpringFormPayload(payload))
.then((res) => {
if (!(res && res.success)) return;
ElMessage.success("操作成功");
back();
})
.finally(() => {
submitLoading.value = false;
});
} else {
updateGoodsParams(buildSpringFormPayload(payload))
.then((res) => {
if (!(res && res.success)) return;
ElMessage.success("操作成功");
back();
})
.finally(() => {
submitLoading.value = false;
});
}
});
}
onMounted(() => {
getCategoryTree().then((res) => {
if (res && res.success) {
categoryTreeSource.value = res.result || [];
const map = {};
buildCategoryIdNameMap(categoryTreeSource.value, map);
categoryIdNameMap.value = map;
}
});
initForm();
});
</script>
<style lang="scss">

View File

@@ -2,7 +2,7 @@
<div class="search">
<el-card>
<el-form
ref="searchForm"
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
@@ -27,7 +27,7 @@
<div class="operation padding-row">
<el-button type="primary" @click="goAdd">添加</el-button>
</div>
<el-table v-loading="loading" border :data="data" ref="table" style="width: 100%">
<el-table v-loading="loading" border :data="data" ref="tableRef" style="width: 100%">
<el-table-column prop="paramName" label="参数名称" width="300" />
<el-table-column prop="options" label="参数值" min-width="260" show-overflow-tooltip />
<el-table-column label="必填" width="300" align="center">
@@ -65,83 +65,93 @@
</el-card>
</div>
</template>
<script>
<script setup>
import { ref, reactive, onMounted } from "vue";
import { useRouter } from "vue-router";
import { deleteParams, getGoodsParamsPage } from "@/api/goods";
import { ElMessage, ElMessageBox } from "element-plus";
export default {
name: "categoryParams",
data() {
return {
submitLoading: false,
loading: true,
total: 0,
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
paramName: "",
},
data: [],
};
},
methods: {
isTruthyFlag(val) {
return val === 1 || val === "1" || val === true;
},
init() {
this.getDataList();
},
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
handleSearch() {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = 20;
this.getDataList();
},
getDataList() {
this.loading = true;
getGoodsParamsPage(this.searchForm).then((res) => {
this.loading = false;
if (res && res.success) {
this.data = (res.result && res.result.records) || [];
this.total = (res.result && res.result.total) || 0;
}
});
},
goAdd() {
this.$router.push({ name: "goods-parameter-edit" });
},
goEdit(row) {
if (!row || !row.id) return;
try {
window.sessionStorage.setItem(`goods-parameter-edit:${row.id}`, JSON.stringify(row));
} catch (e) {}
this.$router.push({ name: "goods-parameter-edit", query: { id: row.id } });
},
remove(row) {
ElMessageBox.confirm("您确认要删除 " + (row.paramName || "") + " ?", "确认删除", { type: "warning" }).then(() => {
return deleteParams(row.id).then((res) => {
if (res && res.success) {
ElMessage.success("删除成功");
this.getDataList();
}
});
}).catch(() => {});
},
},
mounted() {
this.init();
},
};
defineOptions({ name: "categoryParams" });
const router = useRouter();
const submitLoading = ref(false);
const loading = ref(true);
const total = ref(0);
const searchForm = reactive({
pageNumber: 1,
pageSize: 20,
sort: "createTime",
order: "desc",
paramName: "",
});
const data = ref([]);
const searchFormRef = ref(null);
const tableRef = ref(null);
function isTruthyFlag(val) {
return val === 1 || val === "1" || val === true;
}
function init() {
getDataList();
}
function changePage(v) {
searchForm.pageNumber = v;
getDataList();
}
function changePageSize(v) {
searchForm.pageNumber = 1;
searchForm.pageSize = v;
getDataList();
}
function handleSearch() {
searchForm.pageNumber = 1;
searchForm.pageSize = 20;
getDataList();
}
function getDataList() {
loading.value = true;
getGoodsParamsPage(searchForm).then((res) => {
loading.value = false;
if (res && res.success) {
data.value = (res.result && res.result.records) || [];
total.value = (res.result && res.result.total) || 0;
}
});
}
function goAdd() {
router.push({ name: "goods-parameter-edit" });
}
function goEdit(row) {
if (!row || !row.id) return;
try {
window.sessionStorage.setItem(`goods-parameter-edit:${row.id}`, JSON.stringify(row));
} catch (e) {}
router.push({ name: "goods-parameter-edit", query: { id: row.id } });
}
function remove(row) {
ElMessageBox.confirm("您确认要删除 " + (row.paramName || "") + " ?", "确认删除", { type: "warning" }).then(() => {
return deleteParams(row.id).then((res) => {
if (res && res.success) {
ElMessage.success("删除成功");
getDataList();
}
});
}).catch(() => {});
}
onMounted(() => {
init();
});
</script>
<style lang="scss">
.link-text {

View File

@@ -2,7 +2,7 @@
<div class="search">
<el-card>
<el-form
ref="searchForm"
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
@@ -170,145 +170,144 @@
</div>
</template>
<script>
<script setup>
import { nextTick, onMounted, ref } from "vue";
import * as API_Member from "@/api/member";
import { ElMessage, ElMessageBox } from "element-plus";
export default {
name: "goods-review",
data() {
return {
infoData: {},
infoFlag: false,
infoTitle: "",
currentReviewId: "",
detailStatusReady: false,
loading: true,
searchForm: {
pageNumber: 1,
pageSize: 20,
goodsName: "",
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
},
data: [],
total: 0,
};
},
methods: {
gradeTagType(grade) {
if (grade === "GOOD") return "success";
if (grade === "MODERATE") return "warning";
return "danger";
},
gradeText(grade) {
if (grade === "GOOD") return "好评";
if (grade === "MODERATE") return "中评";
return "差评";
},
changeSwitchView(status) {
if (!this.detailStatusReady || !this.currentReviewId) {
return;
defineOptions({ name: "goods-review" });
// 模板中的 el-form ref(原 ref="searchForm",为避免与数据 searchForm 同名互相覆盖而改名),script 内未使用
const searchFormRef = ref(null);
const infoData = ref({});
const infoFlag = ref(false);
const infoTitle = ref("");
const currentReviewId = ref("");
const detailStatusReady = ref(false);
const loading = ref(true);
const searchForm = ref({
pageNumber: 1,
pageSize: 20,
goodsName: "",
sort: "createTime",
order: "desc",
startDate: "",
endDate: "",
});
const data = ref([]);
const total = ref(0);
function gradeTagType(grade) {
if (grade === "GOOD") return "success";
if (grade === "MODERATE") return "warning";
return "danger";
}
function gradeText(grade) {
if (grade === "GOOD") return "好评";
if (grade === "MODERATE") return "中评";
return "差评";
}
function changeSwitchView(status) {
if (!detailStatusReady.value || !currentReviewId.value) {
return;
}
API_Member.updateMemberReview(currentReviewId.value, { status }).then((res) => {
if (res.success) {
ElMessage.success("修改成功!");
getDataList();
}
});
}
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 changeSwitch(row) {
if (!row || !row.id) {
return;
}
API_Member.updateMemberReview(row.id, { status: row.status }).then((res) => {
if (res.success) {
getDataList();
}
});
}
function updateTop(v) {
const top = !v.top;
API_Member.updateMemberReviewTop(v.id, { top }).then((res) => {
if (res.success) {
data.value = data.value.map((item) => {
if (item.goodsId !== v.goodsId) {
return item;
}
return {
...item,
top: item.id === v.id ? top : false,
};
});
ElMessage.success(top ? "置顶成功!" : "取消置顶成功!");
init();
}
});
}
function getDataList() {
loading.value = true;
API_Member.getMemberReview(searchForm.value)
.then((res) => {
if (res.success) {
data.value = res.result.records;
total.value = res.result.total;
}
API_Member.updateMemberReview(this.currentReviewId, { status }).then((res) => {
if (res.success) {
ElMessage.success("修改成功!");
this.getDataList();
}
})
.finally(() => {
loading.value = false;
});
}
function info(v) {
currentReviewId.value = v.id;
detailStatusReady.value = false;
infoData.value = {};
infoFlag.value = true;
infoTitle.value = `用户${v.memberName}的评价详情`;
API_Member.getMemberInfoReview(v.id).then((res) => {
if (res.result) {
infoData.value = res.result;
nextTick(() => {
detailStatusReady.value = true;
});
},
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();
},
changeSwitch(row) {
if (!row || !row.id) {
return;
}
API_Member.updateMemberReview(row.id, { status: row.status }).then((res) => {
if (res.success) {
this.getDataList();
}
});
},
updateTop(v) {
const top = !v.top;
API_Member.updateMemberReviewTop(v.id, { top }).then((res) => {
if (res.success) {
this.data = this.data.map((item) => {
if (item.goodsId !== v.goodsId) {
return item;
}
return {
...item,
top: item.id === v.id ? top : false,
};
});
ElMessage.success(top ? "置顶成功!" : "取消置顶成功!");
this.init();
}
});
},
getDataList() {
this.loading = true;
API_Member.getMemberReview(this.searchForm)
.then((res) => {
if (res.success) {
this.data = res.result.records;
this.total = res.result.total;
}
})
.finally(() => {
this.loading = false;
});
},
info(v) {
this.currentReviewId = v.id;
this.detailStatusReady = false;
this.infoData = {};
this.infoFlag = true;
this.infoTitle = `用户${v.memberName}的评价详情`;
API_Member.getMemberInfoReview(v.id).then((res) => {
if (res.result) {
this.infoData = res.result;
this.$nextTick(() => {
this.detailStatusReady = true;
});
}
});
},
onInfoDialogClosed() {
this.detailStatusReady = false;
this.currentReviewId = "";
this.infoData = {};
},
remove(v) {
ElMessageBox.confirm("您确认要删除客户" + v.memberName + "的评论?", "确认删除", { type: "warning" }).then(() => {
return API_Member.delMemberReview(v.id).then(() => {
ElMessage.success("修改成功");
this.init();
});
}).catch(() => {});
},
},
mounted() {
this.init();
},
};
}
});
}
function onInfoDialogClosed() {
detailStatusReady.value = false;
currentReviewId.value = "";
infoData.value = {};
}
function remove(v) {
ElMessageBox.confirm("您确认要删除客户" + v.memberName + "的评论?", "确认删除", { type: "warning" }).then(() => {
return API_Member.delMemberReview(v.id).then(() => {
ElMessage.success("修改成功");
init();
});
}).catch(() => {});
}
onMounted(() => {
init();
});
</script>
<style lang="scss" scoped>

View File

@@ -36,7 +36,7 @@
</el-card>
<el-dialog v-model="addFlag" title="添加商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<el-form ref="addForm" :model="formAdd" :rules="rulesAdd" label-width="90px">
<el-form ref="addFormRef" :model="formAdd" :rules="rulesAdd" label-width="90px">
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
<el-input v-model="formAdd.groupName" maxlength="30" placeholder="请输入分组名称" />
</el-form-item>
@@ -51,7 +51,7 @@
</el-dialog>
<el-dialog v-model="editFlag" title="编辑商品分组" width="500px" :close-on-click-modal="false" destroy-on-close>
<el-form ref="editForm" :model="formEdit" :rules="rulesEdit" label-width="90px">
<el-form ref="editFormRef" :model="formEdit" :rules="rulesEdit" label-width="90px">
<el-input v-model="formEdit.id" style="display: none" />
<el-form-item label="分组名称" prop="groupName" style="width: 90%">
<el-input v-model="formEdit.groupName" maxlength="30" placeholder="请输入分组名称" />
@@ -68,166 +68,173 @@
</div>
</template>
<script>
<script setup>
import { ref, reactive, nextTick, onMounted } from "vue";
import * as API_Goods from "@/api/goods.js";
import { ElMessage, ElMessageBox } from "element-plus";
export default {
name: "goodsGroup",
data() {
return {
loading: true,
searchForm: {
pageNumber: 1,
pageSize: 20,
},
data: [],
total: 0,
addFlag: false,
editFlag: false,
submitAddLoading: false,
submitEditLoading: false,
formAdd: {
groupName: "",
description: "",
},
formEdit: {
id: "",
groupName: "",
description: "",
},
rulesAdd: {
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
},
rulesEdit: {
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
},
};
},
methods: {
init() {
this.getData();
},
changePage(v) {
this.searchForm.pageNumber = v;
this.getData();
},
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getData();
},
getData() {
this.loading = true;
API_Goods.getGoodsGroupByPage(this.searchForm)
.then((res) => {
this.loading = false;
if (res && res.success && res.result) {
this.data = res.result.records || [];
this.total = res.result.total || 0;
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
this.loading = false;
ElMessage.error("加载商品分组失败");
});
},
openAdd() {
this.addFlag = true;
this.$nextTick(() => {
if (this.$refs.addForm) this.$refs.addForm.resetFields();
this.formAdd = { groupName: "", description: "" };
defineOptions({ name: "goodsGroup" });
const loading = ref(true);
const searchForm = reactive({
pageNumber: 1,
pageSize: 20,
});
const data = ref([]);
const total = ref(0);
const addFlag = ref(false);
const editFlag = ref(false);
const submitAddLoading = ref(false);
const submitEditLoading = ref(false);
const formAdd = ref({
groupName: "",
description: "",
});
const formEdit = ref({
id: "",
groupName: "",
description: "",
});
const rulesAdd = reactive({
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
});
const rulesEdit = reactive({
groupName: [{ required: true, message: "请输入分组名称", trigger: "blur" }],
});
const addFormRef = ref(null);
const editFormRef = ref(null);
function init() {
getData();
}
function changePage(v) {
searchForm.pageNumber = v;
getData();
}
function changePageSize(v) {
searchForm.pageNumber = 1;
searchForm.pageSize = v;
getData();
}
function getData() {
loading.value = true;
API_Goods.getGoodsGroupByPage(searchForm)
.then((res) => {
loading.value = false;
if (res && res.success && res.result) {
data.value = res.result.records || [];
total.value = res.result.total || 0;
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
loading.value = false;
ElMessage.error("加载商品分组失败");
});
}
function openAdd() {
addFlag.value = true;
nextTick(() => {
if (addFormRef.value) addFormRef.value.resetFields();
formAdd.value = { groupName: "", description: "" };
});
}
function submitAdd() {
addFormRef.value.validate((valid) => {
if (!valid) return;
submitAddLoading.value = true;
API_Goods.addGoodsGroup(formAdd.value)
.then((res) => {
submitAddLoading.value = false;
if (res && res.success) {
ElMessage.success("添加成功");
addFlag.value = false;
getData();
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
submitAddLoading.value = false;
ElMessage.error("添加失败,请检查权限或后端接口");
});
},
submitAdd() {
this.$refs.addForm.validate((valid) => {
if (!valid) return;
this.submitAddLoading = true;
API_Goods.addGoodsGroup(this.formAdd)
.then((res) => {
this.submitAddLoading = false;
if (res && res.success) {
ElMessage.success("添加成功");
this.addFlag = false;
this.getData();
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
this.submitAddLoading = false;
ElMessage.error("添加失败,请检查权限或后端接口");
});
});
}
function openEdit(row) {
editFlag.value = true;
submitEditLoading.value = false;
API_Goods.getGoodsGroup(row.id)
.then((res) => {
if (res && res.success && res.result) {
formEdit.value = {
id: res.result.id,
groupName: res.result.groupName || "",
description: res.result.description || "",
};
} else {
formEdit.value = {
id: row.id,
groupName: row.groupName || "",
description: row.description || "",
};
}
})
.catch(() => {
ElMessage.error("获取分组详情失败");
});
}
function submitEdit() {
editFormRef.value.validate((valid) => {
if (!valid) return;
submitEditLoading.value = true;
const { id, groupName, description } = formEdit.value;
API_Goods.updateGoodsGroup(id, { id, groupName, description })
.then((res) => {
submitEditLoading.value = false;
if (res && res.success) {
ElMessage.success("修改成功");
editFlag.value = false;
getData();
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
submitEditLoading.value = false;
ElMessage.error("修改失败,请检查权限或后端接口");
});
},
openEdit(row) {
this.editFlag = true;
this.submitEditLoading = false;
API_Goods.getGoodsGroup(row.id)
.then((res) => {
if (res && res.success && res.result) {
this.formEdit = {
id: res.result.id,
groupName: res.result.groupName || "",
description: res.result.description || "",
};
} else {
this.formEdit = {
id: row.id,
groupName: row.groupName || "",
description: row.description || "",
};
}
})
.catch(() => {
ElMessage.error("获取分组详情失败");
});
},
submitEdit() {
this.$refs.editForm.validate((valid) => {
if (!valid) return;
this.submitEditLoading = true;
const { id, groupName, description } = this.formEdit;
API_Goods.updateGoodsGroup(id, { id, groupName, description })
.then((res) => {
this.submitEditLoading = false;
if (res && res.success) {
ElMessage.success("修改成功");
this.editFlag = false;
this.getData();
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
this.submitEditLoading = false;
ElMessage.error("修改失败,请检查权限或后端接口");
});
});
}
function remove(row) {
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
return API_Goods.deleteGoodsGroup(row.id)
.then((res) => {
if (res && res.success) {
ElMessage.success("删除成功");
getData();
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
ElMessage.error("删除失败,请检查权限或后端接口");
});
},
remove(row) {
ElMessageBox.confirm("确定删除该分组?", "提示", { type: "warning" }).then(() => {
return API_Goods.deleteGoodsGroup(row.id)
.then((res) => {
if (res && res.success) {
ElMessage.success("删除成功");
this.getData();
} else if (res && res.message) {
ElMessage.error(res.message);
}
})
.catch(() => {
ElMessage.error("删除失败,请检查权限或后端接口");
});
}).catch(() => {});
},
},
mounted() {
this.init();
},
};
}).catch(() => {});
}
onMounted(() => {
init();
});
</script>
<style scoped>

View File

@@ -2,7 +2,7 @@
<div class="search">
<el-card>
<el-form
ref="searchForm"
ref="searchFormRef"
:model="searchForm"
inline
label-width="70px"
@@ -68,7 +68,7 @@
</div>
<el-table
ref="table"
ref="tableRef"
v-loading="loading"
:data="data"
class="mt_10"
@@ -208,7 +208,8 @@
</div>
</template>
<script>
<script setup>
import { onMounted, reactive, ref } from "vue";
import {
batchUpdateGoodsSkuVirtualSales,
getGoodsSkuData,
@@ -225,223 +226,222 @@ const VIRTUAL_SALES_FIELDS = [
"salesVolume",
];
export default {
name: "goodsVirtualSales",
data() {
return {
loading: true,
editModalVisible: false,
modalSubmitting: false,
modalMode: "single",
currentSku: null,
currentIndex: -1,
selectedRows: [],
editForm: {
virtualSales: 0,
},
searchForm: {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
goodsName: "",
goodsId: "",
sn: "",
storeName: "",
},
data: [],
total: 0,
};
},
methods: {
init() {
this.getDataList();
},
changePage(v) {
this.searchForm.pageNumber = v;
this.getDataList();
},
changePageSize(v) {
this.searchForm.pageNumber = 1;
this.searchForm.pageSize = v;
this.getDataList();
},
handleSearch() {
this.searchForm.pageNumber = 1;
this.getDataList();
},
handleReset() {
this.searchForm = {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
goodsName: "",
goodsId: "",
sn: "",
storeName: "",
};
this.getDataList();
},
changeSort({ prop, order }) {
if (!order) {
this.searchForm.sort = "create_time";
this.searchForm.order = "desc";
} else {
this.searchForm.sort = prop;
this.searchForm.order = order === "ascending" ? "asc" : "desc";
}
this.getDataList();
},
handleSelectionChange(selection) {
this.selectedRows = selection;
},
getDataList() {
this.loading = true;
getGoodsSkuData(this.searchForm)
.then((res) => {
if (res && res.success && res.result) {
this.data = (res.result.records || []).map((item) => ({
...item,
virtualSalesInput: this.getVirtualSalesValue(item),
saving: false,
}));
this.total = res.result.total || 0;
this.selectedRows = [];
} else {
this.data = [];
this.total = 0;
this.selectedRows = [];
ElMessage.error((res && res.message) || "加载规格列表失败");
}
})
.catch(() => {
this.data = [];
this.total = 0;
this.selectedRows = [];
ElMessage.error("加载规格列表失败");
})
.finally(() => {
this.loading = false;
});
},
getVirtualSalesValue(row) {
const field = VIRTUAL_SALES_FIELDS.find(
(item) => row[item] !== undefined && row[item] !== null && row[item] !== ""
);
return field ? Number(row[field]) || 0 : 0;
},
openVirtualSalesModal(row, index) {
this.modalMode = "single";
this.currentSku = { ...row };
this.currentIndex = index;
this.editForm.virtualSales = this.getVirtualSalesValue(row);
this.editModalVisible = true;
},
openBatchVirtualSalesModal() {
if (!this.selectedRows.length) {
ElMessage.warning("请先选择要设置的商品规格");
return;
}
this.modalMode = "batch";
this.currentSku = null;
this.currentIndex = -1;
this.editForm.virtualSales = 0;
this.editModalVisible = true;
},
handleCancelVirtualSales() {
if (this.modalSubmitting) return;
this.resetVirtualSalesModal();
},
resetVirtualSalesModal() {
this.editModalVisible = false;
this.modalMode = "single";
this.currentSku = null;
this.currentIndex = -1;
this.editForm.virtualSales = 0;
},
handleSubmitVirtualSales() {
const virtualSales = Number(this.editForm.virtualSales);
if (!Number.isInteger(virtualSales) || virtualSales < 0) {
ElMessage.warning("请输入大于等于 0 的整数虚拟销量");
return;
}
if (this.modalMode === "batch") {
this.handleBatchSubmitVirtualSales(virtualSales);
return;
}
if (!this.currentSku || this.currentIndex < 0) return;
defineOptions({ name: "goodsVirtualSales" });
const currentIndex = this.currentIndex;
const currentSkuId = this.currentSku.id;
this.modalSubmitting = true;
if (this.data[currentIndex]) {
this.data[currentIndex].saving = true;
// 模板 ref(避免与同名数据字段冲突而改名)
const searchFormRef = ref(null);
const tableRef = ref(null);
const loading = ref(true);
const editModalVisible = ref(false);
const modalSubmitting = ref(false);
const modalMode = ref("single");
const currentSku = ref(null);
const currentIndex = ref(-1);
const selectedRows = ref([]);
const editForm = reactive({
virtualSales: 0,
});
const searchForm = ref({
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
goodsName: "",
goodsId: "",
sn: "",
storeName: "",
});
const data = ref([]);
const total = ref(0);
function init() {
getDataList();
}
function changePage(v) {
searchForm.value.pageNumber = v;
getDataList();
}
function changePageSize(v) {
searchForm.value.pageNumber = 1;
searchForm.value.pageSize = v;
getDataList();
}
function handleSearch() {
searchForm.value.pageNumber = 1;
getDataList();
}
function handleReset() {
searchForm.value = {
pageNumber: 1,
pageSize: 20,
sort: "create_time",
order: "desc",
goodsName: "",
goodsId: "",
sn: "",
storeName: "",
};
getDataList();
}
function changeSort({ prop, order }) {
if (!order) {
searchForm.value.sort = "create_time";
searchForm.value.order = "desc";
} else {
searchForm.value.sort = prop;
searchForm.value.order = order === "ascending" ? "asc" : "desc";
}
getDataList();
}
function handleSelectionChange(selection) {
selectedRows.value = selection;
}
function getDataList() {
loading.value = true;
getGoodsSkuData(searchForm.value)
.then((res) => {
if (res && res.success && res.result) {
data.value = (res.result.records || []).map((item) => ({
...item,
virtualSalesInput: getVirtualSalesValue(item),
saving: false,
}));
total.value = res.result.total || 0;
selectedRows.value = [];
} else {
data.value = [];
total.value = 0;
selectedRows.value = [];
ElMessage.error((res && res.message) || "加载规格列表失败");
}
updateGoodsSkuVirtualSales(currentSkuId, { virtualSales })
.then((res) => {
if (res && res.success) {
if (this.data[currentIndex]) {
this.data[currentIndex].virtualSales = virtualSales;
this.data[currentIndex].virtualSalesInput = virtualSales;
}
ElMessage.success("虚拟销量设置成功");
this.resetVirtualSalesModal();
} else {
ElMessage.error((res && res.message) || "规格虚拟销量设置失败");
}
})
.catch(() => {
ElMessage.error("规格虚拟销量设置失败");
})
.finally(() => {
if (this.data[currentIndex]) {
this.data[currentIndex].saving = false;
}
this.modalSubmitting = false;
});
},
handleBatchSubmitVirtualSales(virtualSales) {
const skuIds = this.selectedRows.map((item) => item.id).filter(Boolean);
if (!skuIds.length) {
ElMessage.warning("请先选择要设置的商品规格");
return;
})
.catch(() => {
data.value = [];
total.value = 0;
selectedRows.value = [];
ElMessage.error("加载规格列表失败");
})
.finally(() => {
loading.value = false;
});
}
function getVirtualSalesValue(row) {
const field = VIRTUAL_SALES_FIELDS.find(
(item) => row[item] !== undefined && row[item] !== null && row[item] !== ""
);
return field ? Number(row[field]) || 0 : 0;
}
function openVirtualSalesModal(row, index) {
modalMode.value = "single";
currentSku.value = { ...row };
currentIndex.value = index;
editForm.virtualSales = getVirtualSalesValue(row);
editModalVisible.value = true;
}
function openBatchVirtualSalesModal() {
if (!selectedRows.value.length) {
ElMessage.warning("请先选择要设置的商品规格");
return;
}
modalMode.value = "batch";
currentSku.value = null;
currentIndex.value = -1;
editForm.virtualSales = 0;
editModalVisible.value = true;
}
function handleCancelVirtualSales() {
if (modalSubmitting.value) return;
resetVirtualSalesModal();
}
function resetVirtualSalesModal() {
editModalVisible.value = false;
modalMode.value = "single";
currentSku.value = null;
currentIndex.value = -1;
editForm.virtualSales = 0;
}
function handleSubmitVirtualSales() {
const virtualSales = Number(editForm.virtualSales);
if (!Number.isInteger(virtualSales) || virtualSales < 0) {
ElMessage.warning("请输入大于等于 0 的整数虚拟销量");
return;
}
if (modalMode.value === "batch") {
handleBatchSubmitVirtualSales(virtualSales);
return;
}
if (!currentSku.value || currentIndex.value < 0) return;
const curIndex = currentIndex.value;
const currentSkuId = currentSku.value.id;
modalSubmitting.value = true;
if (data.value[curIndex]) {
data.value[curIndex].saving = true;
}
updateGoodsSkuVirtualSales(currentSkuId, { virtualSales })
.then((res) => {
if (res && res.success) {
if (data.value[curIndex]) {
data.value[curIndex].virtualSales = virtualSales;
data.value[curIndex].virtualSalesInput = virtualSales;
}
ElMessage.success("虚拟销量设置成功");
resetVirtualSalesModal();
} else {
ElMessage.error((res && res.message) || "规格虚拟销量设置失败");
}
this.modalSubmitting = true;
batchUpdateGoodsSkuVirtualSales({ skuIds, virtualSales })
.then((res) => {
if (res && res.success) {
this.data = this.data.map((item) => {
if (!skuIds.includes(item.id)) return item;
return {
...item,
virtualSales,
virtualSalesInput: virtualSales,
};
});
this.selectedRows = [];
this.$refs.table?.clearSelection();
ElMessage.success("虚拟销量设置成功");
this.resetVirtualSalesModal();
} else {
ElMessage.error((res && res.message) || "虚拟销量设置失败");
}
})
.catch(() => {
ElMessage.error("虚拟销量设置失败");
})
.finally(() => {
this.modalSubmitting = false;
})
.catch(() => {
ElMessage.error("规格虚拟销量设置失败");
})
.finally(() => {
if (data.value[curIndex]) {
data.value[curIndex].saving = false;
}
modalSubmitting.value = false;
});
}
function handleBatchSubmitVirtualSales(virtualSales) {
const skuIds = selectedRows.value.map((item) => item.id).filter(Boolean);
if (!skuIds.length) {
ElMessage.warning("请先选择要设置的商品规格");
return;
}
modalSubmitting.value = true;
batchUpdateGoodsSkuVirtualSales({ skuIds, virtualSales })
.then((res) => {
if (res && res.success) {
data.value = data.value.map((item) => {
if (!skuIds.includes(item.id)) return item;
return {
...item,
virtualSales,
virtualSalesInput: virtualSales,
};
});
},
getTotalSales(row) {
return (Number(row.buyCount) || 0) + (Number(row.virtualSalesInput) || 0);
},
},
mounted() {
this.init();
},
};
selectedRows.value = [];
tableRef.value?.clearSelection();
ElMessage.success("虚拟销量设置成功");
resetVirtualSalesModal();
} else {
ElMessage.error((res && res.message) || "虚拟销量设置失败");
}
})
.catch(() => {
ElMessage.error("虚拟销量设置失败");
})
.finally(() => {
modalSubmitting.value = false;
});
}
function getTotalSales(row) {
return (Number(row.buyCount) || 0) + (Number(row.virtualSalesInput) || 0);
}
onMounted(() => {
init();
});
</script>
<style lang="scss" scoped>