2025-03-26

This commit is contained in:
2025-03-26 11:00:21 +08:00
parent 927d7381b8
commit b45f4950d3
468 changed files with 54473 additions and 124 deletions

View File

@@ -0,0 +1,62 @@
interface FormItem {
prop: string;
label?: string;
placeholder?: string;
type: string;
isCopy?: boolean;
optionProps?: any;
startPlaceholder?: string;
endPlaceholder?: string;
options?: any;
isArray?: boolean;
startDate?: string; //开始时间(传入后台需要的参数)
endDate?: string; //结束时间(传入后台需要的参数)
startProp?: string;
endProp?: string;
isInteger?: boolean;
disabled?: boolean;
}
export const EDIT_FORM_DATA: FormItem[] = [
{
prop: "attr_name",
placeholder: "请输入",
type: "input",
label: "属性: ",
disabled: false
},
{
prop: "propsStr",
placeholder: "请输入",
type: "input",
label: "属性值: ",
disabled: true
},
{
prop: "addAttribute",
placeholder: "请输入",
type: "input",
label: "新增属性值: "
}
];
export const ADD_FORM_DATA: FormItem[] = [
{
prop: "attr_name",
placeholder: "请输入",
type: "input",
label: "属性: "
},
{
prop: "propsStr",
placeholder: "请输入",
type: "input",
label: "属性值: ",
disabled: false
}
];
export const EDIT_RULE_FORM = {
attr_name: "", //属性
propsStr: "", //属性值
addAttribute: "" //新增
};

View File

@@ -0,0 +1,5 @@
import { SEARCH_DATA, RULE_FORM } from "./search";
import { COLUMNS } from "./table";
import { RULES } from "./rules";
import { EDIT_FORM_DATA, EDIT_RULE_FORM, ADD_FORM_DATA } from "./edit";
export { SEARCH_DATA, RULE_FORM, COLUMNS, RULES, EDIT_FORM_DATA, EDIT_RULE_FORM, ADD_FORM_DATA };

View File

@@ -0,0 +1,4 @@
export const RULES = {
attr_name: [{ required: true, message: "产品属性不能为空 ! ", trigger: "blur" }]
// attributeValue: [{ required: true, message: "产品属性值不能为空 ! ", trigger: "blur" }]
};

View File

@@ -0,0 +1,30 @@
interface FormItem {
prop: string;
label?: string;
placeholder?: string;
type: string;
isCopy?: boolean;
optionProps?: any;
startPlaceholder?: string;
endPlaceholder?: string;
options?: any;
isArray?: boolean;
startDate?: string; //开始时间(传入后台需要的参数)
endDate?: string; //结束时间(传入后台需要的参数)
startProp?: string;
endProp?: string;
isInteger?: boolean;
}
export const SEARCH_DATA: FormItem[] = [
{
prop: "keywords",
placeholder: "请输入",
type: "input",
label: "属性: "
}
];
export const RULE_FORM = {
page: 1,
size: 50
};

View File

@@ -0,0 +1,33 @@
import { RenderScope } from "@/components/ProTable/interface";
export const COLUMNS = [
{
align: "center",
fixed: true,
label: "ID",
prop: "id"
},
{
align: "left",
label: "属性",
prop: "attr_name"
},
{
align: "left",
label: "属性值",
prop: "props",
render: (scope: RenderScope<any>): VNode | string | any => {
let arr: any = [];
if (scope.row.props.length) {
scope.row.props.forEach((item: any) => {
arr.push(item.prop_name);
});
return arr.join(",");
} else {
return;
}
}
},
{ prop: "operation", label: "操作", fixed: "right", width: 160 }
];

View File

@@ -0,0 +1,202 @@
<!-- 属性列表 -->
<template>
<div class="table-box">
<div style="padding-bottom: 16px">
<el-button type="primary" @click="handleAdd"> 添加 </el-button>
</div>
<ProTable
ref="proTableRef"
:formData="dataStore.formData"
:columns="dataStore.columns"
:request-api="getProductAttrListApi"
:init-param="dataStore.initParam"
>
<template #operation="scope">
<el-button size="small" type="primary" @click="handleBtnClick(scope.row)" v-if="scope.row.id !== 1"
>编辑</el-button
>
</template>
</ProTable>
<!-- :show-close="false" -->
<el-drawer
v-model="dataStore.visible"
:show-close="true"
:size="600"
:close-on-click-modal="false"
:close-on-press-escape="false"
:before-close="handleBeforeClone"
destroy-on-close
>
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ dataStore.title }}</h4>
</template>
<div>
<rulesForm
:ruleForm="dataStore.editRuleForm"
:formData="dataStore.editFormData"
:rules="dataStore.rules"
ref="formRef"
/>
</div>
<template #footer>
<div style="flex: auto">
<el-button @click="handleResetClick">重置</el-button>
<el-button type="primary" @click="handleConfirmClick">确认</el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts" name="productAttributeListIndex">
import ProTable from "@/components/ProTable/index.vue";
import rulesForm from "@/components/rulesForm/index.vue";
import qs from "qs";
//接口
import {
getProductAttrListApi,
getProductAttrDetailsApi,
getProductAttrUpApi,
getProductAttrAddApi
} from "@/api/modules/productAttributeList";
//深拷贝方法
import { cloneDeep } from "lodash-es";
//表格和搜索條件
import { RULE_FORM, COLUMNS, RULES, SEARCH_DATA, EDIT_FORM_DATA, EDIT_RULE_FORM, ADD_FORM_DATA } from "./constant/index";
import { useMsg } from "@/hooks/useMsg";
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTableRef = ref<any>(null);
// 数据源
const dataStore = reactive<any>({
title: "编辑产品属性",
rules: cloneDeep(RULES), //抽屉表单验证
row: {},
editRuleForm: cloneDeep(EDIT_RULE_FORM),
editFormData: cloneDeep(EDIT_FORM_DATA), //抽屉表单配置项
visible: false, //抽屉控制
columns: COLUMNS, //列表配置项
initParam: cloneDeep(RULE_FORM), // 初始化搜索条件|重置搜索条件
ruleForm: cloneDeep(RULE_FORM), // 搜索參數
formData: SEARCH_DATA //搜索配置项
});
const formRef: any = ref(null);
//设置表单
const handleSetRuleForm = () => {
dataStore.editRuleForm.attributeKey = dataStore.row.productName;
dataStore.editRuleForm.attributeValue = dataStore.row.model;
};
//编辑
const handleBtnClick = (row: any) => {
dataStore.visible = true;
dataStore.title = "编辑产品属性";
dataStore.editFormData = cloneDeep(EDIT_FORM_DATA);
dataStore.row = cloneDeep(row);
dataStore.editFormData[0].disabled = true;
handleSetRuleForm();
getProductAttrDetails(row.id);
};
//更新
const getProductAttrUp = async () => {
let obj = {
prop_name: dataStore.editRuleForm.addAttribute,
prop_value: dataStore.editRuleForm.addAttribute
};
if (obj.prop_name) {
dataStore.editRuleForm.props.push(obj);
}
const result = await getProductAttrUpApi(dataStore.editRuleForm);
if (result?.code === 0) {
useMsg("success", result?.msg);
proTableRef?.value?.getTableList();
dataStore.visible = false;
handleClear();
// getProductAttrDetails(dataStore.editRuleForm.id);
}
};
//添加
const getProductAttrAdd = async () => {
let params = {
attr_name: dataStore.editRuleForm.attr_name,
props: JSON.stringify([
{
prop_name: dataStore.editRuleForm.propsStr,
prop_value: dataStore.editRuleForm.propsStr
}
])
};
const result = await getProductAttrAddApi(qs.stringify(params));
if (result?.code === 0) {
useMsg("success", result?.msg);
proTableRef?.value?.getTableList();
dataStore.visible = false;
handleClear();
}
};
//详情
const getProductAttrDetails = async (id: any) => {
const result = await getProductAttrDetailsApi(id);
if (result?.code === 0) {
dataStore.editRuleForm = cloneDeep(result?.data);
if (dataStore.editRuleForm.props.length) {
let arr: any = [];
dataStore.editRuleForm.props.forEach((item: any) => {
arr.push(item.prop_name);
});
dataStore.editRuleForm.propsStr = arr.length ? arr.join(",") : [];
}
}
};
//清空表单数据
const handleClear = () => {
for (let key in dataStore.editRuleForm) {
dataStore.editRuleForm[key] = "";
}
};
//添加
const handleAdd = () => {
dataStore.visible = true;
dataStore.title = "添加产品属性";
dataStore.editFormData = cloneDeep(ADD_FORM_DATA);
handleClear();
};
//重置验证
const resetFields = () => {
if (!formRef.value!.ruleFormRef) return;
formRef!.value!.ruleFormRef.resetFields();
};
//抽屉关闭前的钩子
const handleBeforeClone = () => {
dataStore.visible = false;
dataStore.editRuleForm.addAttribute = "";
resetFields();
handleClear();
};
//重置按钮
const handleResetClick = () => {
if (dataStore.title === "添加产品属性") {
dataStore.editRuleForm.addAttribute = "";
handleSetRuleForm();
resetFields();
} else {
getProductAttrDetails(dataStore.row.id);
}
};
//确认
const handleConfirmClick = async () => {
if (!formRef.value!.ruleFormRef) return;
formRef!.value!.ruleFormRef!.validate((valid: any) => {
if (valid) {
dataStore.title === "添加产品属性" ? getProductAttrAdd() : getProductAttrUp();
} else {
console.log("error submit!");
return false;
}
});
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,3 @@
import { handleSubmit } from "./submit";
import { handleReset } from "./reset";
export { handleSubmit, handleReset };

View File

@@ -0,0 +1,7 @@
import { cloneDeep } from "lodash-es";
import { messageBox } from "@/utils/messageBox";
export const handleReset = (dataStore: any) => {
messageBox("该操作会将数据重置为初始状态", () => {
dataStore.basicInfoRuleForm = cloneDeep(dataStore.resetBasicInfoRuleForm);
});
};

View File

@@ -0,0 +1,34 @@
import { useMsg } from "@/hooks/useMsg";
const WARN: any = {
name: "产品名称不能为空 !",
model: "型号不能为空 !",
type: "产品分类不能为空 !",
sort: "产品排序不能为空 !"
};
const warnFunction = (data: any) => {
if (!data.name) {
useMsg("warning", WARN["name"]);
return false;
}
if (!data.model) {
useMsg("warning", WARN["model"]);
return false;
}
if (!data.type) {
useMsg("warning", WARN["type"]);
return false;
}
if (!data.name) {
useMsg("warning", WARN["sort"]);
return false;
}
return true;
};
export const handleSubmit = async (infoRef: any) => {
console.log(infoRef.ruleForm, "============ruleForm===========");
let is = await warnFunction(infoRef.ruleForm);
if (!is) {
return false;
}
};

View File

@@ -0,0 +1,16 @@
import { ElMessageBox } from "element-plus";
export const messageBox = (message: any, callback: any) => {
ElMessageBox.confirm(message, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
console.log("132323");
callback && callback();
})
.catch(() => {
console.log("取消323232");
});
};

View File

@@ -0,0 +1,123 @@
//新增产品分类
export const FORM_DATA_LV1: any[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
label: "产品分类名称: "
},
{
prop: "pid",
placeholder: "请选择",
type: "treeSelect",
label: "所属分类: ",
options: []
},
{
prop: "sort",
placeholder: "请输入",
type: "inputNumber",
label: "产品分类排序: "
},
{
prop: "is_show",
placeholder: "请输入",
type: "radio",
label: "是否显示: ",
options: [
{
label: "是",
value: 1
},
{
label: "否",
value: 0
}
]
}
];
//编辑产品分类
export const EDIT_FORM_DATA_LV1: any[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
label: "产品分类名称: "
},
{
prop: "pid",
placeholder: "请选择",
type: "treeSelect",
label: "所属分类: ",
options: []
},
{
prop: "sort",
placeholder: "请输入",
type: "inputNumber",
label: "产品分类排序: "
},
{
prop: "is_show",
placeholder: "请输入",
type: "radio",
label: "是否显示: ",
options: [
{
label: "是",
value: 1
},
{
label: "否",
value: 0
}
]
}
];
export const FORM_DATA_LV2: any[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
label: "产品分类名称: "
},
{
prop: "pid",
placeholder: "请选择",
type: "treeSelect",
label: "所属分类: ",
options: []
},
{
prop: "related_tco_category",
placeholder: "请选择",
type: "treeSelects",
label: "对应成本系统分类: ",
options: []
},
{
prop: "sort",
placeholder: "请输入",
type: "inputNumber",
label: "产品分类排序: "
},
{
prop: "is_show",
placeholder: "请输入",
type: "radio",
label: "是否显示: ",
options: [
{
label: "是",
value: 1
},
{
label: "否",
value: 0
}
]
}
];

View File

@@ -0,0 +1,5 @@
import { FORM_DATA_LV1, FORM_DATA_LV2 } from "./form";
import { RULES_LV1, RULES_LV2 } from "./rules";
import { RULE_FORM_LV1, RULE_FORM_LV2 } from "./ruleForm";
import { SEARCH } from "./search";
export { FORM_DATA_LV1, FORM_DATA_LV2, RULES_LV1, RULES_LV2, RULE_FORM_LV1, RULE_FORM_LV2, SEARCH };

View File

@@ -0,0 +1,6 @@
export const RULE_FORM_LV1: any = {
sort: 1
};
export const RULE_FORM_LV2: any = {
sort: 1
};

View File

@@ -0,0 +1,15 @@
export const RULES_LV1 = {
name: [{ required: true, message: "产品分类名称不能为空 ! ", trigger: "blur" }],
pid: [{ required: true, message: "所属分类不能为空 ! ", trigger: "change" }],
sort: [{ required: true, message: "产品分类排序不能为空 ! ", trigger: "blur" }],
is_show: [{ required: true, message: "请选择是否展示 ! ", trigger: "change" }],
related_tco_category: [{ required: true, message: "所属分类不能为空 ! ", trigger: "change" }]
};
export const RULES_LV2 = {
name: [{ required: true, message: "产品分类名称不能为空 ! ", trigger: "blur" }],
// id: [{ required: true, message: "产品分类ID不能为空 ! ", trigger: "blur" }],
pid: [{ required: true, message: "所属分类不能为空 ! ", trigger: "change" }],
costSystemType: [{ required: true, message: "对应成本系统分类不能为空 ! ", trigger: "change" }],
sort: [{ required: true, message: "产品分类排序不能为空 ! ", trigger: "blur" }],
is_show: [{ required: true, message: "请选择是否展示 ! ", trigger: "change" }]
};

View File

@@ -0,0 +1,9 @@
export const SEARCH = [
{
prop: "keywords",
placeholder: "请输入",
type: "input",
isArray: true,
label: "分类名称"
}
];

View File

@@ -0,0 +1,291 @@
<!-- 分类列表 -->
<template>
<div class="table-box">
<div class="card table-main">
<SearchForm
:search="search"
:reset="reset"
:formData="dataStore.searchFormData"
:search-param="dataStore.searchParam"
/>
<el-table
:data="dataStore.tableData"
style="width: 100%; margin-bottom: 20px; font-size: 14px"
row-key="id"
border
default-expand-all
>
<el-table-column prop="id" label="id" />
<el-table-column prop="name" label="分类名称" />
<el-table-column prop="sort" label="分类排序">
<template #default="{ row }">
<div @click.stop="">
<el-input v-model="row.sort" @blur="handleBlur(row)" @input="handleInput(row)" />
</div>
</template>
</el-table-column>
<el-table-column prop="is_show" label="是否显示">
<template #default="{ row }">
{{ row.is_show === 1 ? "✔️" : "❌" }}
</template>
</el-table-column>
<el-table-column label="操作" :width="350">
<template #default="scope">
<el-button
type="primary"
size="small"
@click="handleAddChild(scope.$index, scope.row)"
v-if="scope.row.level != 3"
>添加子类</el-button
>
<el-button type="primary" size="small" @click="handleEdit(scope.row)">编辑</el-button>
<el-button
type="info"
size="small"
style="cursor: not-allowed"
@click="handleAddProduct(scope.$index, scope.row)"
>添加产品</el-button
>
<el-button
size="small"
type="info"
style="cursor: not-allowed"
@click="handleDelete(scope.$index, scope.row)"
>删除</el-button
>
</template>
</el-table-column>
</el-table>
</div>
<el-drawer
v-model="dataStore.visible"
:show-close="true"
:size="600"
:close-on-click-modal="false"
:close-on-press-escape="false"
:before-close="handleBeforeClone"
destroy-on-close
>
<template #header="{ titleId, titleClass }">
<h4 :id="titleId" :class="titleClass">{{ dataStore.title }}</h4>
</template>
<rulesForm
:ruleForm="dataStore.ruleForm"
:formData="dataStore.formData"
:rules="dataStore.rules"
ref="formRef"
@handleSelectChangeEmits="handleSelectChangeEmits"
@handleTreesSelectChangeEmits="handleTreesSelectChangeEmits"
/>
<template #footer>
<div style="flex: auto">
<el-button @click="handleResetClick">重置</el-button>
<el-button type="primary" @click="handleConfirmClick">提交</el-button>
</div>
</template>
</el-drawer>
</div>
</template>
<script setup lang="ts" name="productClassListIndex">
import SearchForm from "@/components/SearchForm/index.vue";
import rulesForm from "@/components/rulesForm/index.vue";
import { integerRexg } from "@/utils/regexp/index";
//getProductCategoryDelApi
import {
getProductCategoryListApi,
getArticleCategorySaveApi,
getProductClassCategoryReadApi,
getProductCategoryUpdateApi,
getArticleCategorySortApi,
getProductClassTcoTreeApi
} from "@/api/modules/productClass";
import { addLabelValue } from "../list/utils/common/addLabelValue";
import { useMsg } from "@/hooks/useMsg";
import { FORM_DATA_LV1, FORM_DATA_LV2, RULE_FORM_LV1, RULE_FORM_LV2, RULES_LV1, RULES_LV2, SEARCH } from "./constant/index";
import { cloneDeep } from "lodash-es";
const formRef: any = ref(null);
const dataStore = reactive<any>({
visible: false,
title: "添加产品分类",
selectLevel: 1,
searchParam: {}, //搜索参数
searchFormData: cloneDeep(SEARCH), //搜索配置
ruleForm: cloneDeep(RULE_FORM_LV1),
rules: cloneDeep(RULES_LV1),
formData: cloneDeep(FORM_DATA_LV1),
row: {},
tableData: [],
treeData: []
});
//成本系统
const getProductClassTcoTree = async () => {
const result = await getProductClassTcoTreeApi();
if (result?.code === 0) {
FORM_DATA_LV2[2].options = addLabelValue(result?.data);
}
};
getProductClassTcoTree();
//列表接口
const getProductCategoryList = async () => {
const result = await getProductCategoryListApi(dataStore.searchParam);
if (result?.code === 0) {
let tableData = cloneDeep(result?.data);
dataStore.tableData = tableData;
dataStore.treeData = [];
dataStore.treeData = addLabelValue(tableData);
dataStore.treeData.unshift({ value: 0, label: "无" });
}
};
getProductCategoryList();
//添加子类
const getArticleCategorySave = async (params: any) => {
const result = await getArticleCategorySaveApi(params);
if (result?.code === 0) {
useMsg("success", result?.msg);
dataStore.visible = false;
resetFrom();
formRef!.value!.ruleFormRef.resetFields();
getProductCategoryList();
}
};
const handleSelectChangeEmits = (value: any) => {
dataStore.ruleForm.pid = value.id;
};
const handleTreesSelectChangeEmits = (value: any) => {
console.log(value, "=========value=========");
};
//更新接口
const getProductCategoryUpdate = async () => {
dataStore.ruleForm.related_tco_category = dataStore.ruleForm.related_tco_category
? dataStore.ruleForm.related_tco_category.join(",")
: dataStore.ruleForm.related_tco_category;
const result = await getProductCategoryUpdateApi(dataStore.ruleForm);
if (result?.code === 0) {
useMsg("success", result?.msg);
dataStore.visible = false;
resetFrom();
getProductCategoryList();
}
};
//产品详情
const getProductClassCategoryRead = async (id: any) => {
const result = await getProductClassCategoryReadApi(id);
if (result?.code === 0) {
dataStore.ruleForm = result?.data;
if (dataStore.ruleForm.related_tco_category) {
dataStore.ruleForm.related_tco_category = dataStore.ruleForm.related_tco_category
.split(",")
.map((item: any) => Number(item));
}
setFormDatOptions();
getProductCategoryList();
}
};
//排序
const getArticleCategorySort = async (row: any) => {
const result = await getArticleCategorySortApi({ id: row.id, sort: row.sort });
if (result?.code === 0) {
useMsg("success", result?.msg);
getProductCategoryList();
}
};
const search = () => {
getProductCategoryList();
};
const reset = () => {
dataStore.searchParam.keywords = "";
getProductCategoryList();
};
//重置表单配置
const resetFrom = () => {
if (dataStore.selectLevel === 1 || dataStore.selectLevel === 2) {
dataStore.ruleForm = cloneDeep(RULE_FORM_LV1);
dataStore.formData = cloneDeep(FORM_DATA_LV1);
dataStore.rules = RULES_LV1;
}
if (dataStore.selectLevel === 3) {
dataStore.ruleForm = cloneDeep(RULE_FORM_LV2);
dataStore.formData = cloneDeep(FORM_DATA_LV2);
dataStore.rules = RULES_LV2;
}
};
const setFormDatOptions = () => {
// dataStore.formData[1].options = [];
dataStore.formData[1].options = dataStore.treeData;
// dataStore.formData[1].options.unshift({ value: 0, label: "无" });
// console.log("12323");
};
//添加子类
const handleAddChild = (index: any, row: any) => {
dataStore.selectLevel = row.level;
resetFrom();
dataStore.visible = true;
dataStore.title = "添加产品分类";
setFormDatOptions();
// dataStore.formData[1].options.unshift({ value: 0, label: "无" });
// dataStore.formData[1].options = dataStore.treeData;
};
//before-close
const handleBeforeClone = () => {
resetFrom();
formRef!.value!.ruleFormRef.resetFields();
dataStore.visible = false;
};
const handleEdit = (row: any) => {
dataStore.selectLevel = row.level;
dataStore.visible = true;
dataStore.title = "编辑产品分类";
dataStore.row = row;
resetFrom();
getProductClassCategoryRead(row.id);
};
//重置
const handleResetClick = () => {
if (dataStore.title === "添加产品分类") {
formRef!.value!.ruleFormRef.resetFields();
resetFrom();
} else {
getProductClassCategoryRead(dataStore.row.id);
}
};
//提交
const handleConfirmClick = () => {
if (!formRef.value!.ruleFormRef) return;
formRef!.value!.ruleFormRef!.validate((valid: any) => {
if (valid) {
dataStore.title === "编辑产品分类" ? getProductCategoryUpdate() : getArticleCategorySave(dataStore.ruleForm);
console.log("submit!");
} else {
console.log("error submit!");
return false;
}
});
};
//排序input框失焦
const handleBlur = (row: any) => {
getArticleCategorySort(row);
};
//
const handleInput = (row: any) => {
row.sort = integerRexg(row.sort);
};
//添加产品
const handleAddProduct = (index: any, row: any) => {
console.log(index, row, "=========row========");
};
//删除
const handleDelete = (index: any, row: any) => {
// getProductCategoryDel(row.id);
console.log(index, row, "=========row========");
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,4 @@
import { FORM_DATA, RULE_FORM } from "./search";
import { COLUMNS } from "./table";
import { OPERATIONS } from "./operations";
export { FORM_DATA, RULE_FORM, COLUMNS, OPERATIONS };

View File

@@ -0,0 +1,26 @@
export const OPERATIONS = [
{
name: "下架",
name1: "上架",
id: 1,
type: "primary"
},
{
name: "添加SKU",
name1: "添加SKU",
id: 2,
type: "info"
},
{
name: "编辑",
name1: "编辑",
id: 3,
type: "primary"
},
{
name: "删除",
name1: "删除",
id: 4,
type: "info"
}
];

View File

@@ -0,0 +1,38 @@
interface FormItem {
prop: string;
label?: string;
placeholder?: string;
type: string;
isCopy?: boolean;
optionProps?: any;
startPlaceholder?: string;
endPlaceholder?: string;
options?: any;
isArray?: boolean;
startDate?: string; //开始时间(传入后台需要的参数)
endDate?: string; //结束时间(传入后台需要的参数)
startProp?: string;
endProp?: string;
isInteger?: boolean;
}
export const FORM_DATA: FormItem[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
isArray: true,
label: "产品名称: "
},
{
prop: "spu",
placeholder: "请输入",
type: "input",
isArray: true,
label: "型号: "
}
];
export const RULE_FORM = {
page: 1,
size: 50
};

View File

@@ -0,0 +1,40 @@
//import { RenderScope } from "@/components/ProTable/interface";
export const COLUMNS = [
{
align: "center",
fixed: true,
label: "ID",
prop: "id",
width: 80
},
{
align: "left",
label: "产品名称",
prop: "name",
width: 160
},
{
align: "left",
label: "型号",
prop: "spu",
width: 160
},
{
align: "center",
label: "上架状态",
prop: "is_show"
},
{
align: "center",
label: "平台",
prop: "platform_name",
value: ""
},
{
align: "center",
label: "购买链接",
prop: "link",
value: ""
}
];

View File

@@ -0,0 +1,213 @@
<!-- 购买链接 -->
<template>
<div class="table-box">
<div></div>
<div style="display: flex; padding-bottom: 16px">
<el-button type="primary" @click="handleImport"> 导入 </el-button>
<el-button type="primary" @click="handleExport" style="margin-left: 20px"> 导出 </el-button>
</div>
<ProTable
ref="proTableRef"
:formData="dataStore.formData"
:columns="dataStore.columns"
:request-api="getProductBuypassListApi"
:init-param="dataStore.initParam"
>
<template #imgUrl="scope">
<el-image :src="scope.row.imgUrl" style="width: 60px; height: 60px" />
</template>
<template #status="scope">
<el-tag type="success" effect="dark">{{ scope.row.status }}</el-tag>
</template>
<template #is_show="scope">
<el-tag :type="scope.row.is_show === 1 ? 'success' : 'danger'" effect="dark">{{
scope.row.is_show === 1 ? "已上架" : "未上架"
}}</el-tag>
</template>
<template #platform_name="scope">
<div v-for="(it, itIndex) in scope.row.rowspan" :key="itIndex" style="margin-bottom: 10px">
<el-select
v-model="it.platform_name"
class="m-2"
placeholder="请选择"
@change="handleInputOrChange(it, scope, itIndex)"
>
<el-option
v-for="item in dataStore.options"
:key="item.id"
:label="item.platform"
:value="item.platform"
@click="handleOptionItemClick(item, scope)"
/>
</el-select>
</div>
</template>
<template #link="scope">
<div v-for="(it, itIndex) in scope.row.rowspan" :key="itIndex" style="margin-bottom: 10px">
<el-input v-model="it.link" @blur="handleInputOrChange(it, scope, itIndex)"></el-input>
</div>
</template>
</ProTable>
<ImportExcel ref="importRef" />
</div>
</template>
<script setup lang="ts" name="productLinkIndex">
import ProTable from "@/components/ProTable/index.vue";
import { useMsg } from "@/hooks/useMsg";
import { useExport } from "@/hooks/useExport";
// import type { UploadInstance } from "element-plus";
import ImportExcel from "@/components/ImportExcel/index.vue";
import { useUserStore } from "@/stores/modules/user";
//列表接口
// getProductBuypassListImportApi
import {
getProductBuypassListApi,
getProductPlatformsListApi,
getProductBuypassListExportApi,
getProductBuypassUpdateApi,
getProductBuypassListImportApi
} from "@/api/modules/productLink";
const importRef = ref();
//深拷贝方法
import { cloneDeep } from "lodash-es";
//表格和搜索條件
import { RULE_FORM, FORM_DATA, COLUMNS } from "./constant/index";
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTableRef = ref<any>(null);
const userStore = useUserStore();
// 数据源
const dataStore = reactive<any>({
importUrl: import.meta.env.VITE_APP_API_BASE_UPLOAD_URL + "/product/buypass/import",
uploadHeaders: {
Authorization: "Bearer" + " " + userStore.token
},
fileType: [".xlsx", ".xls"],
options: [],
columns: COLUMNS, //列表配置项
initParam: cloneDeep(RULE_FORM), // 初始化搜索条件|重置搜索条件
ruleForm: cloneDeep(RULE_FORM), // 搜索參數
formData: FORM_DATA //搜索配置项
});
//更新
const getProductBuypassUpdate = async (params: any) => {
const result = await getProductBuypassUpdateApi(params);
if (result?.code === 0) {
console.log(result?.data);
} else {
}
};
// const handleChange=()=>{
// }
// const up=(it: any, scope: any, itIndex: any)=>{
// const { link, platform_name, link_id } = it;
// let is = scope.row.rowspan.some((item: any, index: number) => {
// if (index !== itIndex && item.platform_name === platform_name) {
// return true;
// }
// });
// if (is) {
// useMsg("warning", "同型号平台不能重复 !");
// return;
// }
// if (!platform_name) {
// useMsg("warning", "平台不能为空 !");
// return;
// }
// if (!link) {
// useMsg("warning", "链接不能为空 !");
// return;
// }
// //获取ID
// let platform_id: any = null;
// dataStore.options.forEach((item: any) => {
// if (item.platform === platform_name) {
// platform_id = item.id;
// }
// });
// getProductBuypassUpdate({
// id: link_id,
// param: {
// platform_id,
// link
// }
// });
// }
//输入框失焦
const handleInputOrChange = (it: any, scope: any, itIndex: any) => {
const { link, platform_name, link_id } = it;
let is = scope.row.rowspan.some((item: any, index: number) => {
if (index !== itIndex && item.platform_name === platform_name) {
return true;
}
});
if (is) {
useMsg("warning", "同型号平台不能重复 !");
return;
}
if (!platform_name) {
useMsg("warning", "平台不能为空 !");
return;
}
if (!link) {
useMsg("warning", "链接不能为空 !");
return;
}
//获取ID
let platform_id: any = null;
dataStore.options.forEach((item: any) => {
if (item.platform === platform_name) {
platform_id = item.id;
}
});
getProductBuypassUpdate({
id: link_id,
param: {
platform_id,
link
}
});
};
//平台
const getProductPlatformsList = async () => {
const result = await getProductPlatformsListApi();
if (result?.code === 0) {
dataStore.options = result?.data;
}
};
getProductPlatformsList();
const handleOptionItemClick = (item: any, scope: any) => {
console.log(item, "=1=");
console.log(scope, "=2=");
};
//导出
const handleExport = () => {
getProductBuypassListExport();
};
//导出接口
const getProductBuypassListExport = async () => {
const result = await getProductBuypassListExportApi(dataStore.ruleForm);
await useExport(result);
};
const handleImport = () => {
let params = {
title: "数据",
tempApi: "",
importApi: getProductBuypassListImportApi,
proTableRef: proTableRef
};
importRef.value.acceptParams(params);
};
</script>
<style scoped></style>

View File

@@ -0,0 +1,3 @@
import { handleSubmit } from "./submit";
import { handleReset } from "./reset";
export { handleSubmit, handleReset };

View File

@@ -0,0 +1,7 @@
import { cloneDeep } from "lodash-es";
import { messageBox } from "@/utils/messageBox";
export const handleReset = (dataStore: any) => {
messageBox("该操作会将数据重置为初始状态", () => {
dataStore.basicInfoRuleForm = cloneDeep(dataStore.resetBasicInfoRuleForm);
});
};

View File

@@ -0,0 +1,34 @@
import { useMsg } from "@/hooks/useMsg";
const WARN: any = {
name: "产品名称不能为空 !",
model: "型号不能为空 !",
type: "产品分类不能为空 !",
sort: "产品排序不能为空 !"
};
const warnFunction = (data: any) => {
if (!data.name) {
useMsg("warning", WARN["name"]);
return false;
}
if (!data.model) {
useMsg("warning", WARN["model"]);
return false;
}
if (!data.type) {
useMsg("warning", WARN["type"]);
return false;
}
if (!data.name) {
useMsg("warning", WARN["sort"]);
return false;
}
return true;
};
export const handleSubmit = async (infoRef: any) => {
console.log(infoRef.ruleForm, "============ruleForm===========");
let is = await warnFunction(infoRef.ruleForm);
if (!is) {
return false;
}
};

View File

@@ -0,0 +1,16 @@
import { ElMessageBox } from "element-plus";
export const messageBox = (message: any, callback: any) => {
ElMessageBox.confirm(message, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
console.log("132323");
callback && callback();
})
.catch(() => {
console.log("取消323232");
});
};

View File

@@ -0,0 +1,100 @@
<!-- 基本信息 -->
<template>
<div>
<el-form ref="ruleFormRef" :model="_ruleFormParam" label-width="140">
<el-form-item label="产品名称" required>
<el-input v-model="_ruleFormParam.name" style="width: 440px" />
</el-form-item>
<el-form-item label="副标题名称">
<el-input v-model="_ruleFormParam.short_name" style="width: 440px" />
</el-form-item>
<el-form-item label="型号" required>
<el-input v-model="_ruleFormParam.spu" style="width: 440px" />
</el-form-item>
<el-form-item label="产品分类" style="width: 440px" required>
<!-- <el-input v-model="_ruleFormParam.category_id" /> -->
<el-tree-select
clearable
v-model="_ruleFormParam.category_id"
:data="options"
:render-after-expand="false"
show-checkbox
check-strictly
/>
</el-form-item>
<el-form-item label="产品参数">
<el-input v-model="_ruleFormParam.params" style="width: 440px" />
</el-form-item>
<el-form-item label="产品排序" style="width: 440px" required>
<!-- <el-input v-model="_ruleFormParam.sort" /> -->
<el-input-number
:min="1"
:max="9999"
:controls="true"
v-model.trim="_ruleFormParam.sort"
step-strictly
:step="1"
controls-position="right"
></el-input-number>
</el-form-item>
<el-form-item label="是否上架" style="width: 440px">
<el-radio-group v-model="_ruleFormParam.is_show">
<el-radio :value="1" :label="1"></el-radio>
<el-radio :value="0" :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否新品" style="width: 440px">
<el-radio-group v-model="_ruleFormParam.is_new">
<el-radio :value="1" :label="1"></el-radio>
<el-radio :value="0" :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="是否推荐" style="width: 440px">
<el-radio-group v-model="_ruleFormParam.is_hot">
<el-radio :value="1" :label="1"></el-radio>
<el-radio :value="0" :label="0"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="SEO标题">
<el-input v-model="_ruleFormParam.seo_title" style="width: 440px" />
</el-form-item>
<el-form-item label="SEO关键字">
<el-input v-model="_ruleFormParam.seo_keywords" style="width: 440px" />
</el-form-item>
<el-form-item label="SEO描述">
<el-input v-model="_ruleFormParam.seo_desc" style="width: 440px" />
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts" name="basicInfo">
import type { FormInstance } from "element-plus";
const ruleFormRef = ref<FormInstance>();
interface IProps {
data: { [key: string]: any };
options: any[];
}
const props = defineProps<IProps>();
//重置
const reset = () => {
_ruleFormParam = {};
};
let _ruleFormParam: any = computed(() => props.data);
// 暴露给父组件的参数和方法(外部需要什么,都可以从这里暴露出去)
defineExpose({
ruleForm: _ruleFormParam,
reset
});
</script>
<style scoped></style>

View File

@@ -0,0 +1,2 @@
export const FOLDER =
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIUAAABoCAYAAADfADNgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAANmSURBVHhe7dpLb0xhHMfx/zlza4IQJIg0aoGIWwgSCWUiLNhga+Gy8gKwkmBF0peAqKUFKyxdupFY6MJCEIlYuHXhEmHaao/neeZ0dPxmVIgxnfP9LDrnnEm6mPn2uZzTKHEMmOSXUbz4nNiVZ9/C8d3XY+F1Kj0z4/Toz2xfFLvfEdmORbn0ClqtYRQ+gPKt4fSs9XwUh5fn7fT6QnoFrVQXxcTIcObhaHrl//Jx3NnbFV7ROnVRlG9V3Cgxnp61Bx/E5d4i00kL1RYA/W6EaLcgPD96HRkYsbOD7TF6ZUEYKfwHv/Tq1/RSe2Kd0TphpPjdncX/5MP1ax0/avhj/DshinttOG0046e58s1KmE6mQ8zTUZg+/NQxnf/62J38vp5Zkfu84nA/yC/eG312IYro0pf0FFnSbJ1GFAhxTL4f9Hf3pNERqtv+H3ewiQKBv0c1cS+IKFDT/7T68JMoUOOnEb/NJwrUufdmfOrdx4GenB1alrey29fOKnA/YDobqiR24+WYXXjyze6/a3zDcof7nn8ZxbWdpRAFOk/fo1E7+UAfMvptadPp4/iaAkF0sBPu+y03+XeEplH0beZpZKc7tjKfHtVrGMWGeaw/s2D/ksYjRcM1xa7FOevvLaZn6GSrr1Xs/ciPh6HdMyKLhseSpHSZZx+oKrrBg3kCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCCIAoIoIIgCgiggiAKCKCDiossijtIzZN78UjpSzC1RBapWzI6rUWxZwCyCqq2uhVDDuY2FcAEZF0V2dEWhGsWqObFtW5gL15Fdp9blrGdmZFHi+AvPPo7b2usVq4yH95Ex87tiGzrYFY5ri4llboFxsbdkebYimTOnaDa4z/1I1UaKCc8/JXZ0YNgG3vrLdW+hA+3pztnN3W4fOolEMeHxh8T6Ho3a7Vdj9vpLYiNMKx0h7yaCNXNjW+5mhvObCmEN8bOmUSCrzL4DY7H3Znn6h1gAAAAASUVORK5CYII=";

View File

@@ -0,0 +1,45 @@
import { ArrowLeft, FolderAdd, Refresh, UploadFilled, Upload, DeleteFilled } from "@element-plus/icons-vue";
export const ICONS: any = [
{
icon: ArrowLeft,
placement: "top",
content: "上一级",
type: "primary",
is: true
},
{
icon: Refresh,
placement: "top",
content: "刷新",
type: "primary",
is: true
},
{
icon: UploadFilled,
placement: "top",
content: "上传文件",
type: "primary",
is: true
},
{
icon: Upload,
placement: "top",
content: "直接上传",
type: "primary",
is: true
},
{
icon: FolderAdd,
placement: "top",
content: "创建目录",
type: "primary",
is: true
},
{
icon: DeleteFilled,
placement: "top",
content: "删除",
type: "danger",
is: true
}
];

View File

@@ -0,0 +1,4 @@
import { IMG_INFO_COLUMNS, TABLE_ITEM, CAPACITY, COLOR } from "./table";
import { ICONS } from "./icons";
import { FOLDER } from "./folderBase64";
export { IMG_INFO_COLUMNS, TABLE_ITEM, CAPACITY, COLOR, ICONS, FOLDER };

View File

@@ -0,0 +1,55 @@
export const IMG_INFO_COLUMNS = [
{
label: "SKU",
prop: "sku",
disabled: false,
formType: "input",
maxLength: 50,
width: "180"
},
{
label: "主图",
prop: "main_image",
disabled: false,
formType: "img",
width: "130"
},
{
label: "图片",
prop: "photo_album_clone",
disabled: false,
formType: "imgs"
},
{
label: "操作",
prop: "operation",
disabled: false,
isHeaderIcon: false,
width: 160
}
];
//颜色初始值
export const COLOR = {
label: "颜色",
prop: "color",
disabled: false,
formType: "select",
options: [],
width: "240"
};
//容量初始值
export const CAPACITY = {
label: "容器",
prop: "capacity",
disabled: false,
formType: "input",
width: "240"
};
//添加行初始值
export const TABLE_ITEM = {
sku: "",
color: "",
capacity: "",
img: "",
imgs: []
};

View File

@@ -0,0 +1,222 @@
<template>
<div>
<!-- 封面图 -->
<div>
<h5 style="margin: 0; margin-bottom: 16px; font-size: 14px">封面图</h5>
<UploadImg v-model:image-url="imgInfoDataStore.cover_image">
<template #tip>
<div style="width: 150px; text-align: center">图片尺寸800x800</div>
</template>
</UploadImg>
</div>
<el-divider />
<!-- 属性 -->
<div>
<h5 style="margin-bottom: 16px; font-size: 14px">属性</h5>
<div>
<!-- 复选框组 -->
<el-checkbox-group v-model="selectedAttrIds" @change="handleCheckboxChange">
<el-checkbox v-for="attr in attrList" :key="attr.id" :label="attr.id">
{{ attr.attr_name }}
</el-checkbox>
</el-checkbox-group>
</div>
</div>
<el-divider />
<!-- 列表图片 -->
<div>
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px">
<h5 style="margin: 0; font-size: 14px">列表图信息</h5>
<el-button type="primary" size="small" @click="addRow()">添加行</el-button>
</div>
<!-- 表格 -->
<el-table :data="imgInfoDataStore.skus" border>
<el-table-column label="SKU" prop="sku" width="240px">
<template #default="{ row }">
<el-input style="width: 200px" placeholder="请输入" v-model="row.sku"></el-input>
</template>
</el-table-column>
<el-table-column label="主图" prop="main_image" width="130px">
<template #default="{ row }">
<UploadImg height="104px" width="104px" v-model:image-url="row.main_image"> </UploadImg>
</template>
</el-table-column>
<el-table-column v-for="attrId in selectedAttrIds" :key="attrId" :label="findAttrById(attrId)?.attr_name">
<template #default="{ row }">
<el-form :model="row">
<el-form-item v-if="findAttrById(attrId)?.attr_type === 1" style="margin: 0">
<el-select v-model="findAttrObjInRow(row, attrId).attr_value" placeholder="请选择">
<el-option
v-for="prop in findAttrById(attrId)?.props"
:key="prop.prop_value"
:label="prop.prop_name"
:value="prop.prop_value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item v-if="findAttrById(attrId)?.attr_type === 2" style="margin: 0">
<!-- 生成输入框 -->
<el-input v-model="findAttrObjInRow(row, attrId).attr_value" placeholder="请输入"></el-input>
</el-form-item>
</el-form>
</template>
</el-table-column>
<el-table-column label="图片">
<template #default="{ row }">
<UploadImgs height="80px" width="80px" v-model:fileList="row.photo_album_clone"> </UploadImgs>
</template>
</el-table-column>
<!-- 删除行操作列 -->
<el-table-column label="操作" style="display: flex; align-items: center" width="120px">
<template #default="{ $index }">
<el-button type="danger" @click="deleteRow($index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-divider />
<!-- 视频 -->
<div style="margin-bottom: 40px">
<!-- 视频图片 -->
<div>
<h5 style="margin: 0; margin-bottom: 16px; font-size: 14px">视频图</h5>
<UploadImg v-model:image-url="imgInfoDataStore.video_img"> </UploadImg>
</div>
<!-- 视频 -->
<div>
<h5 style="margin: 16px 0; font-size: 14px">视频文件</h5>
<UploadVideo v-model:video-url="imgInfoDataStore.video_url"> </UploadVideo>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="imgInfo">
import { ref, reactive, shallowRef, onMounted } from "vue";
import UploadImg from "@/components/Upload/UploadImg.vue";
import UploadImgs from "@/components/Upload/UploadImgs.vue";
import UploadVideo from "@/components/Upload/UploadVideo.vue";
interface IProps {
imgInfoData: { [key: string]: any };
attrList: any[];
}
const props = defineProps<IProps>();
const imgInfoDataRef = shallowRef(props.imgInfoData);
let imgInfoDataStore = reactive({ ...imgInfoDataRef.value });
// 存储选中的属性 ID
const selectedAttrIds = ref<any>([]);
// 根据 ID 查找属性
const findAttrById = (id: any) => {
return props.attrList.find(attr => attr.id === id);
};
// 在 row 的 attrs 数组中查找对应 attrId 的对象
const findAttrObjInRow = (row: any, attrId: any) => {
console.log(row.attrs, "=======row===========");
let obj = row.attrs.find((item: any) => item.attr_id === attrId.toString());
if (!obj) {
obj = { attr_id: attrId.toString(), attr_value: "" };
row.attrs = [...row.attrs, obj]; // 避免直接修改原数组
}
return obj;
};
// 处理复选框变化事件
const handleCheckboxChange = () => {
imgInfoDataStore.skus.forEach((row: any) => {
const newAttrs: any = [];
selectedAttrIds.value.forEach((attrId: any) => {
const existingAttr = row.attrs.find((item: any) => item.attr_id === attrId.toString());
if (existingAttr) {
newAttrs.push({ ...existingAttr });
} else {
newAttrs.push({ attr_id: attrId.toString(), attr_value: "" });
}
});
row.attrs = newAttrs;
});
};
// 添加新行的方法
const addRow = () => {
const newRow: any = {
attrs: []
};
selectedAttrIds.value.forEach((attrId: any) => {
newRow.attrs.push({ attr_id: attrId?.toString(), attr_value: "" });
});
imgInfoDataStore.skus.push({
sku: "", //SKU
main_image: "", //主图
photo_album: [], //副图 这个是传给后端的
photo_album_clone: [], //附图 这个是本地用的
attrs: []
});
};
// 删除行的方法
const deleteRow = (index: any) => {
imgInfoDataStore.skus.splice(index, 1);
};
const echoData = () => {
const newSkus: any[] = [];
imgInfoDataStore.skus.forEach((backendRow: any) => {
const newRow = {
sku: backendRow.sku,
main_image: backendRow.main_image,
photo_album: backendRow.photo_album,
photo_album_clone: backendRow.photo_album.map((url: any) => {
const name = url.split("/").pop();
return { name, url };
}),
attrs: []
};
backendRow.attrs.forEach((attr: any) => {
const targetAttr = findAttrObjInRow(newRow, attr.attr_id);
targetAttr.attr_value = attr.attr_value;
if (!selectedAttrIds.value.includes(attr.attr_id)) {
selectedAttrIds.value.push(attr.attr_id);
}
});
newSkus.push(newRow);
});
imgInfoDataStore.skus = newSkus;
console.log(imgInfoDataStore, "=imgInfoDataStore=");
handleCheckboxChange();
};
const callEchoDataIfHasValue = () => {
if (Object.keys(imgInfoDataStore).length > 0) {
echoData();
}
};
onMounted(() => {
callEchoDataIfHasValue();
});
watch(
() => props.imgInfoData,
newValue => {
imgInfoDataRef.value = newValue;
Object.keys(imgInfoDataStore).forEach(key => delete imgInfoDataStore[key]);
Object.assign(imgInfoDataStore, newValue);
callEchoDataIfHasValue();
},
{ deep: true }
);
defineExpose({
data: imgInfoDataStore
});
</script>
<style scoped>
/* 可以添加自定义样式 */
</style>

View File

@@ -0,0 +1,158 @@
interface FormItem {
prop: string;
label?: string;
placeholder?: string;
type: string;
isCopy?: boolean;
optionProps?: any;
startPlaceholder?: string;
endPlaceholder?: string;
options?: any;
isArray?: boolean;
startDate?: string; //开始时间(传入后台需要的参数)
endDate?: string; //结束时间(传入后台需要的参数)
startProp?: string;
endProp?: string;
isInteger?: boolean;
disabled?: boolean;
fileList?: any;
prompt?: any;
}
//basicInfoFormData
export const BASIC_INFO_FORM_DATA: FormItem[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
label: "产品名称: "
},
{
prop: "short_name",
placeholder: "请输入",
type: "input",
label: "副标题名称: "
},
{
prop: "spu",
placeholder: "请输入",
type: "input",
label: "型号: "
},
{
prop: "category_id",
placeholder: "请输入",
type: "input",
label: "产品分类: "
},
{
prop: "params",
placeholder: "请输入",
type: "input",
label: "产品参数: "
},
{
prop: "sort",
placeholder: "请输入",
type: "input",
label: "产品排序: "
},
{
prop: "is_sale",
placeholder: "",
type: "radio",
label: "是否上架: ",
options: [
{
label: "是",
value: 1
},
{
label: "否",
value: 0
}
]
},
{
prop: "is_new",
placeholder: "",
type: "radio",
label: "是否新品: ",
options: [
{
label: "是",
value: 1
},
{
label: "否",
value: 0
}
]
},
{
prop: "is_hot",
placeholder: "",
type: "radio",
label: "是否热门: ",
options: [
{
label: "是",
value: 1
},
{
label: "否",
value: 0
}
]
},
{
prop: "seo_title",
placeholder: "请输入",
type: "input",
label: "SEO标题: "
},
{
prop: "seo_keyword",
placeholder: "请输入",
type: "input",
label: "SEO关键字: "
},
{
prop: "seo_desc",
placeholder: "请输入",
type: "input",
label: "SEO描述: "
}
];
export const BASIC_INFO_RULE_FORM = {
seo_desc: "",
seo_keyword: "",
seo_title: "",
is_hot: "",
is_new: "",
is_sale: "",
is_show: "",
sort: 1,
params: "",
category_id: "",
spu: "",
short_name: "",
name: "",
detail: "",
video_url: "",
video_img: "",
desc: "",
cover_image: ""
};
export const BASIC_INFO_RULES = {
name: [{ required: true, message: "产品名称不能为空 ! ", trigger: "blur" }],
spu: [{ required: true, message: "型号不能为空 ! ", trigger: "blur" }],
category_id: [{ required: true, message: "产品分类不能为空 ! ", trigger: "blur" }],
sort: [{ required: true, message: "产品排序不能为空 ! ", trigger: "blur" }]
};
// editRuleForm: {},
//editFormData: [],

View File

@@ -0,0 +1,5 @@
import { FORM_DATA, RULE_FORM } from "./search";
import { COLUMNS } from "./table";
import { OPERATIONS } from "./operations";
import { BASIC_INFO_FORM_DATA, BASIC_INFO_RULE_FORM, BASIC_INFO_RULES } from "./edit";
export { FORM_DATA, RULE_FORM, COLUMNS, OPERATIONS, BASIC_INFO_RULE_FORM, BASIC_INFO_FORM_DATA, BASIC_INFO_RULES };

View File

@@ -0,0 +1,26 @@
export const OPERATIONS = [
{
name: "下架",
name1: "上架",
id: 1,
type: "primary"
},
{
name: "添加SKU",
name1: "添加SKU",
id: 2,
type: "info"
},
{
name: "编辑",
name1: "编辑",
id: 3,
type: "primary"
},
{
name: "删除",
name1: "删除",
id: 4,
type: "info"
}
];

View File

@@ -0,0 +1,22 @@
export const RELATED_INFO_COLUMNS = [
{
label: "型号",
prop: "spu",
disabled: false,
formType: "selectRemote",
options: []
},
{
label: "排序",
prop: "sort",
disabled: false,
formType: "inputNumber"
},
{
label: "操作",
prop: "operation",
disabled: false,
isHeaderIcon: false,
width: 160
}
];

View File

@@ -0,0 +1,90 @@
interface FormItem {
prop: string;
label?: string;
placeholder?: string;
type: string;
isCopy?: boolean;
optionProps?: any;
startPlaceholder?: string;
endPlaceholder?: string;
options?: any;
isArray?: boolean;
startDate?: string; //开始时间(传入后台需要的参数)
endDate?: string; //结束时间(传入后台需要的参数)
startProp?: string;
endProp?: string;
isInteger?: boolean;
}
export const FORM_DATA: FormItem[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
isArray: true,
label: "产品名称: "
},
{
prop: "spu",
placeholder: "请输入",
type: "input",
isArray: true,
label: "型号: "
},
{
prop: "category_id",
placeholder: "请选择",
type: "treeSelect",
isArray: true,
label: "产品分类: ",
options: [
{
value: "1",
label: "Level one 1",
children: [
{
value: "1-1",
label: "Level two 1-1",
children: [
{
value: "1-1-1",
label: "Level three 1-1-1"
}
]
}
]
}
]
},
{
prop: "Time",
type: "daterange",
options: [],
startPlaceholder: "开始日期",
endPlaceholder: "结束日期",
startDate: "created_at",
// endDate: "createEndDate",
label: "添加时间: "
},
{
prop: "is_show",
placeholder: "请选择",
type: "select",
options: [
{
value: 0,
label: "未上架"
},
{
value: 1,
label: "已上架"
}
],
label: "上架状态: "
}
];
export const RULE_FORM = {
page: 1,
size: 50
};

View File

@@ -0,0 +1,102 @@
import { RenderScope } from "@/components/ProTable/interface";
const YES_OR_NO: any = {
0: "❌",
1: "✔️"
};
export const COLUMNS = [
{
align: "center",
fixed: true,
label: "ID",
prop: "id",
width: 80
},
{
align: "center",
label: "图片",
prop: "cover_image",
width: 160
},
{
align: "left",
label: "产品名称",
prop: "name",
width: 160
},
{
align: "left",
label: "型号",
prop: "spu",
width: 160
},
{
align: "left",
label: "产品分类",
prop: "category_name",
width: 160
},
{
align: "left",
label: "产品排序",
prop: "sort",
width: 80
},
{
align: "center",
label: "新品",
prop: "is_new",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return YES_OR_NO[scope.row.is_new];
}
},
{
align: "center",
label: "热门",
prop: "is_hot",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return YES_OR_NO[scope.row.is_hot];
}
},
{
align: "center",
label: "添加时间",
prop: "created_at",
width: 160
},
{
align: "center",
label: "状态",
prop: "status",
width: 80
},
{
align: "center",
label: "在售",
prop: "is_sale",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return YES_OR_NO[scope.row.is_sale];
}
},
{
align: "center",
label: "库存数量",
prop: "stock_qty",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return scope.row.stock_qty === 0 ? "0" : scope.row.stock_qty;
}
},
{
align: "center",
label: "上架状态",
prop: "is_show",
width: 160
},
{ prop: "operation", label: "操作", fixed: "right", width: 300 }
];

View File

@@ -0,0 +1,174 @@
<template>
<!-- style="margin-bottom: 16px" -->
<div class="table-box">
<div style="padding-bottom: 16px">
<el-button @click="handleReset(dataStore)"> 重置 </el-button>
<el-button type="primary" @click="handleSubmit(infoRef, imgInfoRef, dataStore)"> 提交 </el-button>
</div>
<div class="card table-main">
<el-tabs v-model="activeName" class="demo-tabs">
<el-tab-pane label="基本信息" name="basicInfo">
<basicInfo ref="infoRef" :data="dataStore.basicInfoRuleForm" :options="dataStore.options" />
</el-tab-pane>
<el-tab-pane label="图片信息" name="imgInfo">
<imgInfo :imgInfoData="dataStore.imgInfoData" ref="imgInfoRef" :attrList="dataStore.attrList" />
</el-tab-pane>
<el-tab-pane label="详细内容" name="third">
<div style="width: 1280px; margin: 0 auto">
<WangEditor v-model:value="dataStore.detail" />
</div>
</el-tab-pane>
<el-tab-pane label="相关信息及下载" name="related">
<!-- <related ref="relatedRef" :data="dataStore.related" /> -->
<div style="margin-bottom: 16px">
<el-button type="primary" size="small" @click="handleRelatedAdd()">添加行</el-button>
</div>
<FormTable
:columns="dataStore.relatedColumns"
:tableData="dataStore.relatedTableData"
@handleRemote="handleRemote"
@handleRemoteClick="handleRemoteClick"
>
<template #operation="scope">
<el-button type="danger" size="small" @click="handleRelatedDelete(scope)">删除行</el-button>
</template>
</FormTable>
</el-tab-pane>
</el-tabs>
</div>
</div>
</template>
<script setup lang="ts" name="productEditIndex">
import { ref, reactive } from "vue";
//getProductAttrsApi
import { getProductDetailsApi, getProductListApi, getProductAttrsListApi } from "@/api/modules/productList";
import { getProductCategoryListApi } from "@/api/modules/productClass";
import { RELATED_INFO_COLUMNS } from "./constant/related";
import { cloneDeep, debounce } from "lodash-es";
import { messageBox } from "@/utils/messageBox";
import { useMsg } from "@/hooks/useMsg";
import { handleSubmit, handleReset, initDetailParams } from "./utils/edit/index";
import { addLabelValue } from "./utils/common/addLabelValue";
//组件引入
import basicInfo from "./components/basicInfo.vue";
import imgInfo from "./components/imgInfo.vue";
import WangEditor from "@/components/WangEditor/index.vue";
import FormTable from "@/components/FormTable/index.vue";
const $route = useRoute();
//数据集合
const dataStore = reactive<any>({
relatedColumns: cloneDeep(RELATED_INFO_COLUMNS), //相关信息及下载表格配置
relatedTableData: [{ related_product_id: null, sort: 1, spu: "" }], //相关信息下载参数
imgInfoData: {
cover_image: "",
video_url: "",
video_img: "",
skus: []
}, //图片信息
detail: "", //详情
basicInfoRuleForm: {}, //基本信息表单数据
options: [], //产品分类
attrList: [] //产品属性列表
});
const infoRef = ref<any>(null);
const imgInfoRef = ref<any>(null);
const activeName = ref("basicInfo");
//详情
const getProductDetails = async () => {
let id = $route.query.id;
if (!id) {
return;
}
const result = await getProductDetailsApi(id);
if (result?.code === 0) {
const { data } = result;
//参数初始化(将参数按照不同的tab区分出来)
initDetailParams(dataStore, data);
}
};
getProductDetails();
//产品属性列表
const getProductAttrsList = async () => {
const result = await getProductAttrsListApi();
if (result?.code === 0) {
dataStore.attrList = result?.data;
}
};
getProductAttrsList();
//型号接口(后端大佬说用产品列表接口)
const getProductList = async (query: any) => {
const result: any = await getProductListApi({
spu: query,
page: 1,
size: 1000
});
if (result?.code === 0) {
let data = cloneDeep(result?.data?.data);
//参数本地化
let dataClone = data.map((item: any) => {
return {
label: item.spu,
value: item.spu,
id: item.id
};
});
dataStore.relatedColumns[0].options = dataClone;
}
};
//产品分类(后端大佬说直接掉列表接口)
const getProductCategoryList = async () => {
const result = await getProductCategoryListApi({ page: 1, size: 500 });
if (result?.code === 0) {
let dataClone: any = cloneDeep(result?.data);
dataStore.options = addLabelValue(dataClone);
}
};
getProductCategoryList();
//相关信息及行远程搜索下拉框点击
const handleRemoteClick = (params: any) => {
const { item, index } = params;
console.log(item, index);
dataStore.relatedTableData[index].related_product_id = item.id;
};
//相关信息及行删除
const handleRelatedDelete = (scope: any) => {
messageBox("确定要删除该行数据?", () => {
if (dataStore.relatedTableData.length === 1) {
useMsg("warning", "请至少保留一行数据 ! ");
return;
}
dataStore.relatedTableData.splice(scope.$index, 1);
});
};
//相关信息及下载添加行
const handleRelatedAdd = () => {
//添加行的初始化对象
let obj = {
related_product_id: null,
sort: 1,
spu: ""
};
dataStore.relatedTableData.push(obj);
};
//相关信息及下载远程搜索
const handleRemote = debounce((params: any) => {
getProductList(params.query);
}, 800);
//产品属性接口
// const getProductAttrs = async () => {
// const result = await getProductAttrsApi();
// if (result?.code === 0) {
// console.log(result.data);
// }
// };
// getProductAttrs();
</script>
<style scoped></style>

View File

@@ -0,0 +1,155 @@
<!-- 产品列表 -->
<template>
<div class="table-box">
<div style="padding-bottom: 16px">
<el-button type="primary" @click="handleExport"> 导出 </el-button>
</div>
<ProTable
ref="proTableRef"
:formData="dataStore.formData"
:columns="dataStore.columns"
:request-api="getProductListApi"
:init-param="dataStore.initParam"
>
<template #cover_image="scope">
<el-image :src="h + scope.row.cover_image" style="width: 60px; height: 60px" />
</template>
<template #status="scope">
<el-tag effect="dark" :type="scope.row.status === 1 ? 'success' : 'danger'">{{
scope.row.status === 1 ? "启用" : "禁用"
}}</el-tag>
</template>
<template #is_show="scope">
<el-tag :type="scope.row.is_show === 1 ? 'success' : 'danger'" effect="dark">{{
scope.row.is_show === 1 ? "已上架" : "未上架"
}}</el-tag>
</template>
<!-- sort -->
<template #sort="scope">
<el-input v-model="scope.row.sort" @blur="handleBlur(scope.row)" @input="handleInput(scope.row)"></el-input>
</template>
<template #operation="scope">
<el-button
size="small"
:type="scope.row.is_show === 1 ? 'danger' : 'primary'"
@click="handleBtnClick('上下架', scope.row)"
>{{ scope.row.is_show === 1 ? "下架" : "上架" }}</el-button
>
<el-button size="small" type="info" style="cursor: not-allowed" @click="handleBtnClick('添加SKU', scope.row)"
>添加SKU</el-button
>
<el-button size="small" type="primary" @click="handleBtnClick('编辑', scope.row)">编辑</el-button>
<el-button size="small" type="info" style="cursor: not-allowed" @click="handleBtnClick('删除', scope.row)"
>删除</el-button
>
</template>
</ProTable>
</div>
</template>
<script setup lang="ts" name="productListIndex">
import ProTable from "@/components/ProTable/index.vue";
import { integerRexg } from "@/utils/regexp/index";
import { useMsg } from "@/hooks/useMsg";
import { useExport } from "@/hooks/useExport";
const h = import.meta.env.VITE_APP_API_BASE_UPLOAD_URL;
//列表接口
//getProductDelApi
import {
getProductListApi,
getProductUpOrShelvesApi,
getProductListSortApi,
getProductListExportApi
} from "@/api/modules/productList";
import { getProductCategoryListApi } from "@/api/modules/productClass";
import { addLabelValue } from "./utils/common/addLabelValue";
//深拷贝方法
import { cloneDeep } from "lodash-es";
//表格和搜索條件
import { RULE_FORM, FORM_DATA, COLUMNS } from "./constant/index";
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTableRef = ref<any>(null);
const $router = useRouter();
// 数据源
const dataStore = reactive<any>({
columns: COLUMNS, //列表配置项
initParam: cloneDeep(RULE_FORM), // 初始化搜索条件|重置搜索条件
ruleForm: cloneDeep(RULE_FORM), // 搜索參數
formData: FORM_DATA //搜索配置项
});
//导出
const handleExport = () => {
getProductListExport();
};
//导出接口
const getProductListExport = async () => {
const result = await getProductListExportApi(dataStore.ruleForm);
await useExport(result);
};
//上下架
const getProductUpOrShelves = async (id: any) => {
const result = await getProductUpOrShelvesApi(id);
if (result?.code === 0) {
useMsg("success", result?.msg);
proTableRef?.value?.getTableList();
}
};
//排序
const getProductListSort = async (row: any) => {
const result = await getProductListSortApi({ id: row.id, sort: row.sort });
if (result?.code === 0) {
useMsg("success", result?.msg);
proTableRef?.value?.getTableList();
}
};
//产品分类(后端大佬说直接掉列表接口)
const getProductCategoryList = async () => {
const result = await getProductCategoryListApi({ page: 1, size: 500 });
if (result?.code === 0) {
let dataClone: any = cloneDeep(result?.data);
dataStore.formData[2].options = addLabelValue(dataClone);
console.log(result?.data);
}
};
getProductCategoryList();
//排序input框失焦
const handleBlur = (row: any) => {
getProductListSort(row);
};
//
const handleInput = (row: any) => {
row.sort = integerRexg(row.sort);
};
//按钮点击事件
const handleBtnClick = (type: any, row: any) => {
// if (type === "删除") {
// // getProductDel(row.id);
// }
//添加SUK和删除暂时无法操作
if (type == "添加SUK" || type === "删除") {
return false;
}
if (type === "上下架") {
getProductUpOrShelves(row.id);
}
if (type === "编辑") {
$router.push({
path: "/admin/productManagement/list/edit",
query: {
id: row.id
}
});
return;
}
};
</script>
<style scoped></style>
./utils/common/addLabelValue

View File

@@ -0,0 +1,14 @@
export const addLabelValue = (arr: any) => {
return arr.map((item: any) => {
// 为当前对象添加 label 和 value 属性
const newItem = { ...item };
newItem.label = newItem.name;
newItem.value = newItem.id;
// 如果有子对象,递归调用 addLabelValue 处理子对象
if (newItem.children && Array.isArray(newItem.children)) {
newItem.children = addLabelValue(newItem.children);
}
return newItem;
});
};

View File

@@ -0,0 +1,4 @@
import { handleSubmit } from "./submit";
import { handleReset } from "./reset";
import { initDetailParams } from "./initDetailParams";
export { handleSubmit, handleReset, initDetailParams };

View File

@@ -0,0 +1,35 @@
import { cloneDeep } from "lodash-es";
//将参数分离
export const initDetailParams = (dataStore: any, data: any) => {
//基本信息
dataStore.basicInfoRuleForm = cloneDeep({
name: data.name,
short_name: data.short_name,
spu: data.spu,
category_id: data.category_id,
params: data.category_id,
sort: data.sort,
is_show: data.is_show,
is_new: data.is_new,
is_hot: data.is_hot,
is_sale: data.is_sale,
status: data.status,
seo_title: data.seo_title,
seo_keywords: data.seo_keywords,
seo_desc: data.seo_desc,
stock_qty: data.stock_qty,
id: data.id
});
//详情
dataStore.detail = cloneDeep(data.detail);
//图片
dataStore.imgInfoData.cover_image = data.cover_image;
dataStore.imgInfoData.video_url = data.video_url;
dataStore.imgInfoData.skus = data.skus;
dataStore.imgInfoData.video_img = data.video_img;
//相关信息及下载
if (data.related) {
dataStore.relatedTableData = cloneDeep(data.related);
}
};

View File

@@ -0,0 +1,22 @@
import { getProductDetailsApi } from "@/api/modules/productList";
import { useMsg } from "@/hooks/useMsg";
import { messageBox } from "@/utils/messageBox";
// import { cloneDeep } from "lodash-es";
import { initDetailParams } from "./initDetailParams";
//详情(重置,重新获取一下详情)
const getProductDetails = async (dataStore: any) => {
const { id } = dataStore.basicInfoRuleForm;
const result = await getProductDetailsApi(id);
if (result?.code === 0) {
const { data } = result;
initDetailParams(dataStore, data);
useMsg("success", "重置成功 !");
}
};
export const handleReset = (dataStore: any) => {
messageBox("该操作会将数据重置为初始状态", () => {
getProductDetails(dataStore);
});
};

View File

@@ -0,0 +1,78 @@
import { useMsg } from "@/hooks/useMsg";
import { getProductEditUpApi } from "@/api/modules/productList";
import { cloneDeep } from "lodash-es";
const WARN: any = {
name: "产品名称不能为空 !",
spu: "型号不能为空 !",
category_id: "产品分类不能为空 !",
sort: "产品排序不能为空 !"
};
//警告
const warnFunction = (data: any) => {
if (!data.name) {
useMsg("warning", WARN["name"]);
return false;
}
if (!data.spu) {
useMsg("warning", WARN["spu"]);
return false;
}
if (!data.category_id) {
useMsg("warning", WARN["category_id"]);
return false;
}
if (!data.name) {
useMsg("warning", WARN["sort"]);
return false;
}
return true;
};
//更新
const getProductEditUp = async (params: any) => {
const result: any = await getProductEditUpApi(params);
if (result?.code === 0) {
useMsg("success", result?.msg);
// montageImg(imgInfoRef);
}
};
export const handleSubmit = async (infoRef: any, imgInfoRef: any, dataStore: any) => {
let is = await warnFunction(infoRef.ruleForm);
if (!is) {
return false;
}
//相关信息及下载(过滤掉没有id的对象)
let relatedData = dataStore.relatedTableData.filter((item: any) => {
return item.related_product_id;
});
const { video_img, video_url, cover_image, skus } = imgInfoRef.data;
//不要直接去修改skus的数据类型,这里的skus是和表格绑定的
let skusClone = cloneDeep(skus);
skusClone.forEach((item: any) => {
let arr: any = [];
item.photo_album_clone.forEach((it: any) => {
arr.push(it.url);
});
item.photo_album = arr;
item.attrs = item.attrs;
// delete item.photo_albumClone;
});
console.log(skusClone, "=skusClone=");
let skusCloneStr = JSON.stringify(skusClone);
console.log(typeof skusCloneStr);
const params = {
...infoRef.ruleForm,
cover_image,
video_url,
video_img,
skus: skusCloneStr,
detail: dataStore.details,
related: JSON.stringify(relatedData) || []
};
console.log(params, "===========params=============");
getProductEditUp(params);
};

View File

@@ -0,0 +1,4 @@
import { FORM_DATA, RULE_FORM } from "./search";
import { COLUMNS } from "./table";
import { OPERATIONS } from "./operations";
export { FORM_DATA, RULE_FORM, COLUMNS, OPERATIONS };

View File

@@ -0,0 +1,26 @@
export const OPERATIONS = [
{
name: "下架",
name1: "上架",
id: 1,
type: "primary"
},
{
name: "添加SKU",
name1: "添加SKU",
id: 2,
type: "info"
},
{
name: "编辑",
name1: "编辑",
id: 3,
type: "primary"
},
{
name: "删除",
name1: "删除",
id: 4,
type: "info"
}
];

View File

@@ -0,0 +1,63 @@
interface FormItem {
prop: string;
label?: string;
placeholder?: string;
type: string;
isCopy?: boolean;
optionProps?: any;
startPlaceholder?: string;
endPlaceholder?: string;
options?: any;
isArray?: boolean;
startDate?: string; //开始时间(传入后台需要的参数)
endDate?: string; //结束时间(传入后台需要的参数)
startProp?: string;
endProp?: string;
isInteger?: boolean;
}
export const FORM_DATA: FormItem[] = [
{
prop: "name",
placeholder: "请输入",
type: "input",
isArray: true,
label: "产品名称: "
},
{
prop: "spu",
placeholder: "请输入",
type: "input",
isArray: true,
label: "型号: "
},
{
prop: "category_id",
placeholder: "请选择",
type: "treeSelect",
isArray: true,
label: "产品分类: ",
options: [
{
value: "1",
label: "Level one 1",
children: [
{
value: "1-1",
label: "Level two 1-1",
children: [
{
value: "1-1-1",
label: "Level three 1-1-1"
}
]
}
]
}
]
}
];
export const RULE_FORM = {
page: 1,
size: 50
};

View File

@@ -0,0 +1,95 @@
import { RenderScope } from "@/components/ProTable/interface";
const YES_OR_NO: any = {
0: "❌",
1: "✔️"
};
export const COLUMNS = [
{ type: "selection", fixed: "left", width: 40 },
{
align: "center",
fixed: true,
label: "ID",
prop: "id",
width: 80
},
{
align: "center",
label: "图片",
prop: "cover_image",
width: 160
},
{
align: "left",
label: "产品名称",
prop: "name",
width: 160
},
{
align: "left",
label: "型号",
prop: "spu",
width: 160
},
{
align: "left",
label: "产品分类",
prop: "category_name",
width: 160
},
{
align: "left",
label: "产品排序",
prop: "sort",
width: 80
},
{
align: "center",
label: "新品",
prop: "is_new",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return YES_OR_NO[scope.row.is_new];
}
},
{
align: "center",
label: "添加时间",
prop: "created_at",
width: 120
},
{
align: "center",
label: "状态",
prop: "status",
width: 80
},
{
align: "center",
label: "在售",
prop: "is_sale",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return YES_OR_NO[scope.row.is_sale];
}
},
{
align: "center",
label: "库存数量",
prop: "stock_qty",
width: 80,
render: (scope: RenderScope<any>): VNode | string | any => {
return scope.row.stock_qty === 0 ? "0" : scope.row.stock_qty;
}
},
{
align: "center",
label: "上架状态",
prop: "is_show",
render: (scope: RenderScope<any>): VNode | string | any => {
return YES_OR_NO[scope.row.is_show];
}
},
{ prop: "operation", label: "操作", fixed: "right", width: 300 }
];

View File

@@ -0,0 +1,105 @@
<!-- 回收站 -->
<template>
<div class="table-box">
<ProTable
ref="proTableRef"
:formData="dataStore.formData"
:columns="dataStore.columns"
:request-api="getProductTrashListApi"
:init-param="dataStore.initParam"
>
<template #image="scope">
<el-image :src="h + scope.row.image" style="width: 60px; height: 60px" />
</template>
<template #status="scope">
<el-tag type="danger" effect="dark"> {{ scope.row.status === 1 ? "删除" : "删除" }}</el-tag>
</template>
<template #operation="scope">
<el-button size="small" type="danger" @click="handleBtnClick('删除', scope.row)">删除</el-button>
<el-button size="small" type="primary" @click="handleBtnClick('恢复', scope.row)">恢复</el-button>
</template>
</ProTable>
</div>
</template>
<script setup lang="ts" name="productRecycleIndex">
import ProTable from "@/components/ProTable/index.vue";
import { messageBox } from "@/utils/messageBox";
const h = import.meta.env.VITE_APP_API_BASE_UPLOAD_URL;
//列表接口
import { getProductTrashListApi, getProductTrashDelApi, getProductTrashRestoreApi } from "@/api/modules/productRecycle";
//深拷贝方法
import { cloneDeep } from "lodash-es";
//表格和搜索條件
import { RULE_FORM, FORM_DATA, COLUMNS } from "./constant/index";
import { useMsg } from "@/hooks/useMsg";
import { getProductCategoryListApi } from "@/api/modules/productClass";
// 获取 ProTable 元素,调用其获取刷新数据方法(还能获取到当前查询参数,方便导出携带参数)
const proTableRef = ref<any>(null);
// 数据源
const dataStore = reactive<any>({
columns: COLUMNS, //列表配置项
initParam: cloneDeep(RULE_FORM), // 初始化搜索条件|重置搜索条件
ruleForm: cloneDeep(RULE_FORM), // 搜索參數
formData: FORM_DATA //搜索配置项
});
//删除
const getProductTrashDel = async (id: any) => {
messageBox("你确定要删除?", async () => {
const result = await getProductTrashDelApi(id);
if (result?.code === 0) {
useMsg("success", result?.msg);
proTableRef?.value?.getTableList();
}
});
};
//恢复
const getProductTrashRestore = async (id: any) => {
const result = await getProductTrashRestoreApi(id);
if (result?.code === 0) {
useMsg("success", result?.msg);
proTableRef?.value?.getTableList();
}
};
//按钮点击事件
const handleBtnClick = (type: any, row: any) => {
//删除
if (type === "删除") {
getProductTrashDel(row.id);
}
if (type === "恢复") {
getProductTrashRestore(row.id);
}
};
const addLabelValue = (arr: any) => {
return arr.map((item: any) => {
// 为当前对象添加 label 和 value 属性
const newItem = { ...item };
newItem.label = newItem.name;
newItem.value = newItem.id;
// 如果有子对象,递归调用 addLabelValue 处理子对象
if (newItem.children && Array.isArray(newItem.children)) {
newItem.children = addLabelValue(newItem.children);
}
return newItem;
});
};
//产品分类(后端大佬说直接掉列表接口)
const getProductCategoryList = async () => {
const result = await getProductCategoryListApi({ page: 1, size: 500 });
if (result?.code === 0) {
// let data:any = []
let dataClone: any = cloneDeep(result?.data);
dataStore.formData[2].options = addLabelValue(dataClone);
console.log(result?.data);
}
};
getProductCategoryList();
</script>
<style scoped></style>

View File

@@ -0,0 +1,3 @@
import { handleSubmit } from "./submit";
import { handleReset } from "./reset";
export { handleSubmit, handleReset };

View File

@@ -0,0 +1,7 @@
import { cloneDeep } from "lodash-es";
import { messageBox } from "@/utils/messageBox";
export const handleReset = (dataStore: any) => {
messageBox("该操作会将数据重置为初始状态", () => {
dataStore.basicInfoRuleForm = cloneDeep(dataStore.resetBasicInfoRuleForm);
});
};

View File

@@ -0,0 +1,34 @@
import { useMsg } from "@/hooks/useMsg";
const WARN: any = {
name: "产品名称不能为空 !",
model: "型号不能为空 !",
type: "产品分类不能为空 !",
sort: "产品排序不能为空 !"
};
const warnFunction = (data: any) => {
if (!data.name) {
useMsg("warning", WARN["name"]);
return false;
}
if (!data.model) {
useMsg("warning", WARN["model"]);
return false;
}
if (!data.type) {
useMsg("warning", WARN["type"]);
return false;
}
if (!data.name) {
useMsg("warning", WARN["sort"]);
return false;
}
return true;
};
export const handleSubmit = async (infoRef: any) => {
console.log(infoRef.ruleForm, "============ruleForm===========");
let is = await warnFunction(infoRef.ruleForm);
if (!is) {
return false;
}
};

View File

@@ -0,0 +1,16 @@
import { ElMessageBox } from "element-plus";
export const messageBox = (message: any, callback: any) => {
ElMessageBox.confirm(message, "警告", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
console.log("132323");
callback && callback();
})
.catch(() => {
console.log("取消323232");
});
};