|
|
@@ -6,31 +6,25 @@ import {
|
|
|
createLayerTemplate,
|
|
|
updateLayerTemplate
|
|
|
} from "@/api/layer-template";
|
|
|
-import { getProductList } from "@/api/product";
|
|
|
+import type { FloorConfig, GoodsItem } from "@/api/layer-template";
|
|
|
import type { FormInstance, FormRules } from "element-plus";
|
|
|
|
|
|
-/**
|
|
|
- * 楼层配置项类型定义
|
|
|
- */
|
|
|
-interface FloorItem {
|
|
|
- /** 层号 / Floor number */
|
|
|
- floor: number;
|
|
|
- /** 该层选择的商品ID数组 / Selected product IDs for this floor */
|
|
|
- goods: number[];
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* 表单数据类型
|
|
|
*/
|
|
|
interface FormData {
|
|
|
- /** 设备ID / Device ID */
|
|
|
- deviceId: string | number;
|
|
|
- /** 模板名称 / Template name */
|
|
|
+ /** 模板名称 */
|
|
|
templateName: string;
|
|
|
- /** 左门楼层配置 / Left door floor configurations */
|
|
|
- leftFloors: FloorItem[];
|
|
|
- /** 右门楼层配置 / Right door floor configurations */
|
|
|
- rightFloors: FloorItem[];
|
|
|
+ /** 设备类型: 1-静态柜(单门), 2-动态柜(双门) */
|
|
|
+ deviceType: number;
|
|
|
+ /** 模板类型: 1-层模板 */
|
|
|
+ templateType: number;
|
|
|
+ /** 设备层数 */
|
|
|
+ shelfNum: number;
|
|
|
+ /** 左门楼层配置 */
|
|
|
+ leftFloors: FloorConfig[];
|
|
|
+ /** 右门楼层配置 */
|
|
|
+ rightFloors: FloorConfig[];
|
|
|
}
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
@@ -45,36 +39,28 @@ const title = ref("新增层模版");
|
|
|
const isEdit = computed(() => title.value === "修改");
|
|
|
// 编辑时的记录ID
|
|
|
const editId = ref<number | null>(null);
|
|
|
+// 编辑时的设备ID
|
|
|
+const editDeviceId = ref<string>("");
|
|
|
|
|
|
// 表单引用
|
|
|
const formRef = ref<FormInstance>();
|
|
|
-// 商品列表数据
|
|
|
-const productList = ref<any[]>([]);
|
|
|
-// 商品加载状态
|
|
|
-const productLoading = ref(false);
|
|
|
+// 当前选中的层 Tab
|
|
|
+const activeFloorTab = ref("left-1");
|
|
|
+// 右门当前选中的层 Tab
|
|
|
+const activeRightFloorTab = ref("right-1");
|
|
|
|
|
|
// 表单数据
|
|
|
const form = reactive<FormData>({
|
|
|
- deviceId: "",
|
|
|
templateName: "",
|
|
|
+ deviceType: 1,
|
|
|
+ templateType: 1,
|
|
|
+ shelfNum: 5,
|
|
|
leftFloors: [],
|
|
|
rightFloors: []
|
|
|
});
|
|
|
|
|
|
// 表单校验规则
|
|
|
const rules = computed<FormRules>(() => ({
|
|
|
- deviceId: [
|
|
|
- {
|
|
|
- required: true,
|
|
|
- message: "请输入设备ID",
|
|
|
- trigger: "blur"
|
|
|
- },
|
|
|
- {
|
|
|
- pattern: /^[0-9]+$/,
|
|
|
- message: "设备ID必须为数字",
|
|
|
- trigger: "blur"
|
|
|
- }
|
|
|
- ],
|
|
|
templateName: [
|
|
|
{
|
|
|
required: true,
|
|
|
@@ -87,97 +73,182 @@ const rules = computed<FormRules>(() => ({
|
|
|
message: "模板名称长度在2到50个字符之间",
|
|
|
trigger: "blur"
|
|
|
}
|
|
|
+ ],
|
|
|
+ shelfNum: [
|
|
|
+ {
|
|
|
+ required: true,
|
|
|
+ message: "请输入设备层数",
|
|
|
+ trigger: "blur"
|
|
|
+ }
|
|
|
]
|
|
|
}));
|
|
|
|
|
|
/**
|
|
|
- * 获取商品列表
|
|
|
- * Fetch product list for selection
|
|
|
+ * 是否为双门柜
|
|
|
*/
|
|
|
-async function fetchProductList() {
|
|
|
- productLoading.value = true;
|
|
|
- try {
|
|
|
- const { data } = await getProductList({
|
|
|
- page: 1,
|
|
|
- pageSize: 1000 // 获取足够多的商品供选择
|
|
|
- });
|
|
|
- productList.value = data.list || [];
|
|
|
- } catch (error) {
|
|
|
- console.error("获取商品列表失败:", error);
|
|
|
- message("获取商品列表失败", { type: "error" });
|
|
|
- } finally {
|
|
|
- productLoading.value = false;
|
|
|
- }
|
|
|
-}
|
|
|
+const isDoubleDoor = computed(() => form.deviceType === 2);
|
|
|
|
|
|
/**
|
|
|
* 重置表单数据
|
|
|
- * Reset form data to initial state
|
|
|
*/
|
|
|
function resetForm() {
|
|
|
- form.deviceId = "";
|
|
|
form.templateName = "";
|
|
|
+ form.deviceType = 1;
|
|
|
+ form.templateType = 1;
|
|
|
+ form.shelfNum = 5;
|
|
|
form.leftFloors = [];
|
|
|
form.rightFloors = [];
|
|
|
editId.value = null;
|
|
|
+ editDeviceId.value = "";
|
|
|
+ activeFloorTab.value = "left-1";
|
|
|
+ activeRightFloorTab.value = "right-1";
|
|
|
nextTick(() => {
|
|
|
formRef.value?.clearValidate();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * 根据 shelfNum 初始化层数据
|
|
|
+ * 当用户修改层数时,自动调整层数据
|
|
|
+ */
|
|
|
+function initFloorsByShelfNum() {
|
|
|
+ const num = form.shelfNum || 0;
|
|
|
+
|
|
|
+ // 调整左门层数
|
|
|
+ while (form.leftFloors.length < num) {
|
|
|
+ form.leftFloors.push({
|
|
|
+ floor: form.leftFloors.length + 1,
|
|
|
+ goods: []
|
|
|
+ });
|
|
|
+ }
|
|
|
+ while (form.leftFloors.length > num) {
|
|
|
+ form.leftFloors.pop();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 调整右门层数
|
|
|
+ if (isDoubleDoor.value) {
|
|
|
+ while (form.rightFloors.length < num) {
|
|
|
+ form.rightFloors.push({
|
|
|
+ floor: form.rightFloors.length + 1,
|
|
|
+ goods: []
|
|
|
+ });
|
|
|
+ }
|
|
|
+ while (form.rightFloors.length > num) {
|
|
|
+ form.rightFloors.pop();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ form.rightFloors = [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新默认 Tab
|
|
|
+ if (form.leftFloors.length > 0) {
|
|
|
+ activeFloorTab.value = `left-${form.leftFloors[0].floor}`;
|
|
|
+ }
|
|
|
+ if (form.rightFloors.length > 0) {
|
|
|
+ activeRightFloorTab.value = `right-${form.rightFloors[0].floor}`;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 监听 shelfNum 变化,自动调整层数据
|
|
|
+ */
|
|
|
+watch(
|
|
|
+ () => form.shelfNum,
|
|
|
+ () => {
|
|
|
+ if (form.shelfNum > 0 && form.shelfNum <= 20) {
|
|
|
+ initFloorsByShelfNum();
|
|
|
+ }
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
+/**
|
|
|
+ * 监听设备类型变化
|
|
|
+ */
|
|
|
+watch(
|
|
|
+ () => form.deviceType,
|
|
|
+ () => {
|
|
|
+ initFloorsByShelfNum();
|
|
|
+ }
|
|
|
+);
|
|
|
+
|
|
|
/**
|
|
|
* 打开对话框
|
|
|
- * Open dialog
|
|
|
- * @param dialogTitle - 对话框标题
|
|
|
- * @param row - 编辑时的行数据(可选)
|
|
|
*/
|
|
|
async function open(dialogTitle?: string, row?: any) {
|
|
|
title.value = dialogTitle || "新增层模版";
|
|
|
resetForm();
|
|
|
visible.value = true;
|
|
|
|
|
|
- // 加载商品列表
|
|
|
- await fetchProductList();
|
|
|
-
|
|
|
// 如果是编辑模式,回显数据
|
|
|
if (row && row.id) {
|
|
|
editId.value = row.id;
|
|
|
+ editDeviceId.value = row.deviceId || "";
|
|
|
await loadTemplateData(row.id);
|
|
|
+ } else {
|
|
|
+ // 新增模式,初始化默认层数据
|
|
|
+ initFloorsByShelfNum();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 加载模版详情数据
|
|
|
- * Load template detail data by ID
|
|
|
- * @param id - 模版ID
|
|
|
*/
|
|
|
async function loadTemplateData(id: number) {
|
|
|
try {
|
|
|
- const { data } = await getLayerTemplateById(id);
|
|
|
+ const res = await getLayerTemplateById(id);
|
|
|
+ const data = res.data;
|
|
|
if (data) {
|
|
|
- form.deviceId = data.deviceId || "";
|
|
|
form.templateName = data.templateName || "";
|
|
|
+ form.deviceType = data.deviceType || 1;
|
|
|
+ form.shelfNum = data.shelfNum || 5;
|
|
|
+
|
|
|
+ // 解析左门层数据(JSON字符串 -> FloorConfig数组)
|
|
|
+ if (data.leftFloors) {
|
|
|
+ try {
|
|
|
+ const parsed = typeof data.leftFloors === "string"
|
|
|
+ ? JSON.parse(data.leftFloors)
|
|
|
+ : data.leftFloors;
|
|
|
+ if (Array.isArray(parsed)) {
|
|
|
+ form.leftFloors = parsed.map((item: any) => ({
|
|
|
+ floor: typeof item.floor === "string" ? parseInt(item.floor, 10) : (item.floor || 0),
|
|
|
+ goods: Array.isArray(item.goods) ? item.goods : []
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error("解析左门层数据失败:", e);
|
|
|
+ form.leftFloors = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- // 解析左右门楼层配置
|
|
|
- if (data.floorConfigs && Array.isArray(data.floorConfigs)) {
|
|
|
- // 假设前半部分为左门,后半部分为右门(根据实际业务逻辑调整)
|
|
|
- const midPoint = Math.ceil(data.floorConfigs.length / 2);
|
|
|
- form.leftFloors = data.floorConfigs
|
|
|
- .slice(0, midPoint)
|
|
|
- .map((config: any) => ({
|
|
|
- floor: config.floorNumber || config.name || 0,
|
|
|
- goods:
|
|
|
- config.products?.map((p: any) => p.productId).filter(
|
|
|
- (id: number) => id != null
|
|
|
- ) || []
|
|
|
- }));
|
|
|
- form.rightFloors = data.floorConfigs.slice(midPoint).map((config: any) => ({
|
|
|
- floor: config.floorNumber || config.name || 0,
|
|
|
- goods:
|
|
|
- config.products?.map((p: any) => p.productId).filter(
|
|
|
- (id: number) => id != null
|
|
|
- ) || []
|
|
|
- }));
|
|
|
+ // 解析右门层数据
|
|
|
+ if (data.rightFloors) {
|
|
|
+ try {
|
|
|
+ const parsed = typeof data.rightFloors === "string"
|
|
|
+ ? JSON.parse(data.rightFloors)
|
|
|
+ : data.rightFloors;
|
|
|
+ if (Array.isArray(parsed)) {
|
|
|
+ form.rightFloors = parsed.map((item: any) => ({
|
|
|
+ floor: typeof item.floor === "string" ? parseInt(item.floor, 10) : (item.floor || 0),
|
|
|
+ goods: Array.isArray(item.goods) ? item.goods : []
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ console.error("解析右门层数据失败:", e);
|
|
|
+ form.rightFloors = [];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有层数据但有 shelfNum,则初始化空层
|
|
|
+ if (form.leftFloors.length === 0 && form.shelfNum > 0) {
|
|
|
+ initFloorsByShelfNum();
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置默认 Tab
|
|
|
+ if (form.leftFloors.length > 0) {
|
|
|
+ activeFloorTab.value = `left-${form.leftFloors[0].floor}`;
|
|
|
+ }
|
|
|
+ if (form.rightFloors.length > 0) {
|
|
|
+ activeRightFloorTab.value = `right-${form.rightFloors[0].floor}`;
|
|
|
}
|
|
|
}
|
|
|
} catch (error) {
|
|
|
@@ -187,111 +258,75 @@ async function loadTemplateData(id: number) {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 添加左门楼层
|
|
|
- * Add left door floor
|
|
|
- */
|
|
|
-function addLeftFloor() {
|
|
|
- const nextFloor =
|
|
|
- form.leftFloors.length > 0
|
|
|
- ? Math.max(...form.leftFloors.map(f => f.floor)) + 1
|
|
|
- : 1;
|
|
|
- form.leftFloors.push({
|
|
|
- floor: nextFloor,
|
|
|
- goods: []
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 删除左门楼层
|
|
|
- * Remove left door floor
|
|
|
- * @param index - 要删除的索引
|
|
|
+ * 删除某层中的商品
|
|
|
*/
|
|
|
-function removeLeftFloor(index: number) {
|
|
|
- form.leftFloors.splice(index, 1);
|
|
|
- // 重新编号
|
|
|
- form.leftFloors.forEach((floor, idx) => {
|
|
|
- floor.floor = idx + 1;
|
|
|
- });
|
|
|
+function removeGoods(floorList: FloorConfig[], floorIndex: number, goodsIndex: number) {
|
|
|
+ floorList[floorIndex].goods?.splice(goodsIndex, 1);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 添加右门楼层
|
|
|
- * Add right door floor
|
|
|
+ * 添加商品到某层(弹出输入框填写商品信息)
|
|
|
*/
|
|
|
-function addRightFloor() {
|
|
|
- const nextFloor =
|
|
|
- form.rightFloors.length > 0
|
|
|
- ? Math.max(...form.rightFloors.map(f => f.floor)) + 1
|
|
|
- : 1;
|
|
|
- form.rightFloors.push({
|
|
|
- floor: nextFloor,
|
|
|
- goods: []
|
|
|
- });
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 删除右门楼层
|
|
|
- * Remove right door floor
|
|
|
- * @param index - 要删除的索引
|
|
|
- */
|
|
|
-function removeRightFloor(index: number) {
|
|
|
- form.rightFloors.splice(index, 1);
|
|
|
- // 重新编号
|
|
|
- form.rightFloors.forEach((floor, idx) => {
|
|
|
- floor.floor = idx + 1;
|
|
|
+function addGoods(floorList: FloorConfig[], floorIndex: number) {
|
|
|
+ const floor = floorList[floorIndex];
|
|
|
+ if (!floor.goods) {
|
|
|
+ floor.goods = [];
|
|
|
+ }
|
|
|
+ floor.goods.push({
|
|
|
+ key: "",
|
|
|
+ label: "",
|
|
|
+ stock: 0,
|
|
|
+ c_price: "",
|
|
|
+ pic: "",
|
|
|
+ type: ""
|
|
|
});
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 构造提交数据格式
|
|
|
- * Build submit data format matching backend DTO
|
|
|
+ * 构造提交数据格式(匹配哈哈平台 postLayerTypeInfo 接口格式)
|
|
|
*/
|
|
|
function buildSubmitData() {
|
|
|
- // 构造楼层配置数组
|
|
|
- const floorConfigs = [
|
|
|
- ...form.leftFloors.map(f => ({
|
|
|
- floorNumber: f.floor,
|
|
|
- name: `左门第${f.floor}层`,
|
|
|
- products: f.goods.map(productId => ({
|
|
|
- productId,
|
|
|
- quantity: 1,
|
|
|
- sortOrder: 0
|
|
|
- }))
|
|
|
- })),
|
|
|
- ...form.rightFloors.map(f => ({
|
|
|
- floorNumber: f.floor,
|
|
|
- name: `右门第${f.floor}层`,
|
|
|
- products: f.goods.map(productId => ({
|
|
|
- productId,
|
|
|
- quantity: 1,
|
|
|
- sortOrder: 0
|
|
|
- }))
|
|
|
- }))
|
|
|
- ];
|
|
|
+ // 构造哈哈平台所需的 products 结构
|
|
|
+ const products: Record<string, any> = {};
|
|
|
+
|
|
|
+ // 左门层数据
|
|
|
+ products.left = form.leftFloors.map(f => ({
|
|
|
+ floor: f.floor,
|
|
|
+ goods: (f.goods || []).map(g => g.key).filter(k => k && k.trim() !== "")
|
|
|
+ }));
|
|
|
+
|
|
|
+ // 右门层数据(双门柜才有)
|
|
|
+ if (isDoubleDoor.value && form.rightFloors.length > 0) {
|
|
|
+ products.right = form.rightFloors.map(f => ({
|
|
|
+ floor: f.floor,
|
|
|
+ goods: (f.goods || []).map(g => g.key).filter(k => k && k.trim() !== "")
|
|
|
+ }));
|
|
|
+ }
|
|
|
|
|
|
return {
|
|
|
- deviceId: parseInt(form.deviceId.toString(), 10),
|
|
|
+ deviceId: editDeviceId.value,
|
|
|
templateName: form.templateName.trim(),
|
|
|
- totalFloors: form.leftFloors.length + form.rightFloors.length,
|
|
|
- floorConfigs,
|
|
|
- status: 1 // 默认启用
|
|
|
+ deviceType: form.deviceType,
|
|
|
+ shelfNum: form.shelfNum,
|
|
|
+ leftFloors: form.leftFloors,
|
|
|
+ rightFloors: isDoubleDoor.value ? form.rightFloors : [],
|
|
|
+ // 保存完整的 products JSON(用于推送到哈哈平台)
|
|
|
+ productsJson: JSON.stringify(products)
|
|
|
};
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 提交表单
|
|
|
- * Submit form
|
|
|
*/
|
|
|
async function handleSubmit() {
|
|
|
if (!formRef.value) return;
|
|
|
|
|
|
try {
|
|
|
- // 表单校验
|
|
|
await formRef.value.validate();
|
|
|
|
|
|
// 至少需要一层配置
|
|
|
- if (form.leftFloors.length === 0 && form.rightFloors.length === 0) {
|
|
|
- message("请至少添加一个楼层的配置", { type: "warning" });
|
|
|
+ if (form.leftFloors.length === 0) {
|
|
|
+ message("请至少配置一个楼层", { type: "warning" });
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
@@ -299,26 +334,23 @@ async function handleSubmit() {
|
|
|
let res;
|
|
|
|
|
|
if (isEdit.value && editId.value) {
|
|
|
- // 更新操作
|
|
|
res = await updateLayerTemplate(editId.value, submitData);
|
|
|
} else {
|
|
|
- // 新增操作
|
|
|
res = await createLayerTemplate(submitData);
|
|
|
}
|
|
|
|
|
|
- if (res.code === 0) {
|
|
|
+ if (res.code === 200 || res.code === 0) {
|
|
|
message(
|
|
|
`${isEdit.value ? "修改" : "新增"}层模版成功`,
|
|
|
{ type: "success" }
|
|
|
);
|
|
|
visible.value = false;
|
|
|
- emit("success"); // 通知父组件刷新列表
|
|
|
+ emit("success");
|
|
|
} else {
|
|
|
message(res.message || "操作失败", { type: "error" });
|
|
|
}
|
|
|
} catch (error: any) {
|
|
|
if (error !== false) {
|
|
|
- // 非校验错误
|
|
|
console.error("提交表单失败:", error);
|
|
|
message("提交失败,请检查输入", { type: "error" });
|
|
|
}
|
|
|
@@ -327,7 +359,6 @@ async function handleSubmit() {
|
|
|
|
|
|
/**
|
|
|
* 关闭对话框
|
|
|
- * Close dialog
|
|
|
*/
|
|
|
function handleClose() {
|
|
|
visible.value = false;
|
|
|
@@ -344,7 +375,7 @@ defineExpose({
|
|
|
<el-dialog
|
|
|
v-model="visible"
|
|
|
:title="title"
|
|
|
- width="800px"
|
|
|
+ width="900px"
|
|
|
destroy-on-close
|
|
|
:close-on-click-modal="false"
|
|
|
@close="handleClose"
|
|
|
@@ -359,136 +390,282 @@ defineExpose({
|
|
|
<!-- 基本信息 -->
|
|
|
<el-divider content-position="left">基本信息</el-divider>
|
|
|
|
|
|
- <el-form-item label="设备ID" prop="deviceId">
|
|
|
- <el-input
|
|
|
- v-model="form.deviceId"
|
|
|
- placeholder="请输入设备ID"
|
|
|
- :disabled="isEdit"
|
|
|
- clearable
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <el-form-item label="模板名称" prop="templateName">
|
|
|
- <el-input
|
|
|
- v-model="form.templateName"
|
|
|
- placeholder="请输入模板名称"
|
|
|
- clearable
|
|
|
- maxlength="50"
|
|
|
- show-word-limit
|
|
|
- />
|
|
|
- </el-form-item>
|
|
|
-
|
|
|
- <!-- 左门配置 -->
|
|
|
- <el-divider content-position="left">左门配置</el-divider>
|
|
|
-
|
|
|
- <div class="floors-container">
|
|
|
- <div
|
|
|
- v-for="(floor, index) in form.leftFloors"
|
|
|
- :key="'left-' + index"
|
|
|
- class="floor-card"
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="模板名称" prop="templateName">
|
|
|
+ <el-input
|
|
|
+ v-model="form.templateName"
|
|
|
+ placeholder="请输入模板名称"
|
|
|
+ clearable
|
|
|
+ maxlength="50"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="设备层数" prop="shelfNum">
|
|
|
+ <el-input-number
|
|
|
+ v-model="form.shelfNum"
|
|
|
+ :min="1"
|
|
|
+ :max="20"
|
|
|
+ :step="1"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 100%"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <el-row :gutter="20">
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="设备类型">
|
|
|
+ <el-radio-group v-model="form.deviceType">
|
|
|
+ <el-radio :value="1">单门柜</el-radio>
|
|
|
+ <el-radio :value="2">双门柜</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="模板类型">
|
|
|
+ <el-radio-group v-model="form.templateType">
|
|
|
+ <el-radio :value="1">层模板</el-radio>
|
|
|
+ <el-radio :value="2">柜模板</el-radio>
|
|
|
+ </el-radio-group>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+
|
|
|
+ <!-- 商品模板 - 左门 -->
|
|
|
+ <el-divider content-position="left">
|
|
|
+ 商品模板 {{ isDoubleDoor ? "- 左门" : "" }}
|
|
|
+ </el-divider>
|
|
|
+
|
|
|
+ <el-tabs v-model="activeFloorTab" type="border-card">
|
|
|
+ <el-tab-pane
|
|
|
+ v-for="(floor, fIndex) in form.leftFloors"
|
|
|
+ :key="'left-' + floor.floor"
|
|
|
+ :label="'第' + floor.floor + '层'"
|
|
|
+ :name="'left-' + floor.floor"
|
|
|
>
|
|
|
- <el-card shadow="hover">
|
|
|
- <template #header>
|
|
|
- <div class="card-header">
|
|
|
- <span>第 {{ floor.floor }} 层</span>
|
|
|
- <el-button
|
|
|
- type="danger"
|
|
|
- size="small"
|
|
|
- text
|
|
|
- @click="removeLeftFloor(index)"
|
|
|
- >
|
|
|
- 删除此层
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-select
|
|
|
- v-model="floor.goods"
|
|
|
- multiple
|
|
|
- filterable
|
|
|
- collapse-tags
|
|
|
- collapse-tags-tooltip
|
|
|
- placeholder="选择该层的商品"
|
|
|
- :loading="productLoading"
|
|
|
- class="w-full!"
|
|
|
+ <div class="floor-toolbar">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ plain
|
|
|
+ @click="addGoods(form.leftFloors, fIndex)"
|
|
|
>
|
|
|
- <el-option
|
|
|
- v-for="product in productList"
|
|
|
- :key="product.id"
|
|
|
- :label="`${product.name} (${product.barcode || '无条码'})`"
|
|
|
- :value="product.id"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-card>
|
|
|
- </div>
|
|
|
-
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- plain
|
|
|
- class="add-floor-btn"
|
|
|
- @click="addLeftFloor"
|
|
|
- >
|
|
|
- + 添加左门层
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 右门配置 -->
|
|
|
- <el-divider content-position="left">右门配置</el-divider>
|
|
|
-
|
|
|
- <div class="floors-container">
|
|
|
- <div
|
|
|
- v-for="(floor, index) in form.rightFloors"
|
|
|
- :key="'right-' + index"
|
|
|
- class="floor-card"
|
|
|
- >
|
|
|
- <el-card shadow="hover">
|
|
|
- <template #header>
|
|
|
- <div class="card-header">
|
|
|
- <span>第 {{ floor.floor }} 层</span>
|
|
|
+ + 添加商品
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-table
|
|
|
+ :data="floor.goods || []"
|
|
|
+ border
|
|
|
+ size="small"
|
|
|
+ style="width: 100%"
|
|
|
+ empty-text="暂无商品,请点击添加商品"
|
|
|
+ >
|
|
|
+ <el-table-column label="商品图片" width="80" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-image
|
|
|
+ v-if="row.pic"
|
|
|
+ :src="row.pic"
|
|
|
+ :preview-src-list="[row.pic]"
|
|
|
+ fit="cover"
|
|
|
+ style="width: 50px; height: 50px; border-radius: 4px"
|
|
|
+ preview-teleported
|
|
|
+ />
|
|
|
+ <span v-else class="text-gray-400 text-xs">无图片</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品名称" min-width="160">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-if="!row.label || row._editing"
|
|
|
+ v-model="row.label"
|
|
|
+ size="small"
|
|
|
+ placeholder="请输入商品名称"
|
|
|
+ />
|
|
|
+ <span v-else>{{ row.label }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品编码" min-width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-if="!row.key || row._editing"
|
|
|
+ v-model="row.key"
|
|
|
+ size="small"
|
|
|
+ placeholder="商品code"
|
|
|
+ />
|
|
|
+ <span v-else class="text-gray-500">{{ row.key }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品售价" width="100" align="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span>{{ row.c_price || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="优惠价" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-model="row.dis_price"
|
|
|
+ size="small"
|
|
|
+ placeholder="优惠价"
|
|
|
+ clearable
|
|
|
+ style="width: 90px"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="标准库存" width="100">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ v-model="row.stock"
|
|
|
+ size="small"
|
|
|
+ :min="0"
|
|
|
+ :max="9999"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 80px"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="80" align="center" fixed="right">
|
|
|
+ <template #default="{ $index }">
|
|
|
<el-button
|
|
|
type="danger"
|
|
|
size="small"
|
|
|
- text
|
|
|
- @click="removeRightFloor(index)"
|
|
|
+ link
|
|
|
+ @click="removeGoods(form.leftFloors, fIndex, $index)"
|
|
|
>
|
|
|
- 删除此层
|
|
|
+ 删除
|
|
|
</el-button>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <el-select
|
|
|
- v-model="floor.goods"
|
|
|
- multiple
|
|
|
- filterable
|
|
|
- collapse-tags
|
|
|
- collapse-tags-tooltip
|
|
|
- placeholder="选择该层的商品"
|
|
|
- :loading="productLoading"
|
|
|
- class="w-full!"
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+
|
|
|
+ <!-- 商品模板 - 右门(仅双门柜显示) -->
|
|
|
+ <template v-if="isDoubleDoor">
|
|
|
+ <el-divider content-position="left">商品模板 - 右门</el-divider>
|
|
|
+
|
|
|
+ <el-tabs v-model="activeRightFloorTab" type="border-card">
|
|
|
+ <el-tab-pane
|
|
|
+ v-for="(floor, fIndex) in form.rightFloors"
|
|
|
+ :key="'right-' + floor.floor"
|
|
|
+ :label="'第' + floor.floor + '层'"
|
|
|
+ :name="'right-' + floor.floor"
|
|
|
+ >
|
|
|
+ <div class="floor-toolbar">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ size="small"
|
|
|
+ plain
|
|
|
+ @click="addGoods(form.rightFloors, fIndex)"
|
|
|
+ >
|
|
|
+ + 添加商品
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ <el-table
|
|
|
+ :data="floor.goods || []"
|
|
|
+ border
|
|
|
+ size="small"
|
|
|
+ style="width: 100%"
|
|
|
+ empty-text="暂无商品,请点击添加商品"
|
|
|
>
|
|
|
- <el-option
|
|
|
- v-for="product in productList"
|
|
|
- :key="product.id"
|
|
|
- :label="`${product.name} (${product.barcode || '无条码'})`"
|
|
|
- :value="product.id"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
- </el-card>
|
|
|
+ <el-table-column label="商品图片" width="80" align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-image
|
|
|
+ v-if="row.pic"
|
|
|
+ :src="row.pic"
|
|
|
+ :preview-src-list="[row.pic]"
|
|
|
+ fit="cover"
|
|
|
+ style="width: 50px; height: 50px; border-radius: 4px"
|
|
|
+ preview-teleported
|
|
|
+ />
|
|
|
+ <span v-else class="text-gray-400 text-xs">无图片</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品名称" min-width="160">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-if="!row.label || row._editing"
|
|
|
+ v-model="row.label"
|
|
|
+ size="small"
|
|
|
+ placeholder="请输入商品名称"
|
|
|
+ />
|
|
|
+ <span v-else>{{ row.label }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品编码" min-width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-if="!row.key || row._editing"
|
|
|
+ v-model="row.key"
|
|
|
+ size="small"
|
|
|
+ placeholder="商品code"
|
|
|
+ />
|
|
|
+ <span v-else class="text-gray-500">{{ row.key }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="商品售价" width="100" align="right">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <span>{{ row.c_price || '-' }}</span>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="优惠价" width="120">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input
|
|
|
+ v-model="row.dis_price"
|
|
|
+ size="small"
|
|
|
+ placeholder="优惠价"
|
|
|
+ clearable
|
|
|
+ style="width: 90px"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="标准库存" width="100">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ v-model="row.stock"
|
|
|
+ size="small"
|
|
|
+ :min="0"
|
|
|
+ :max="9999"
|
|
|
+ controls-position="right"
|
|
|
+ style="width: 80px"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="操作" width="80" align="center" fixed="right">
|
|
|
+ <template #default="{ $index }">
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ size="small"
|
|
|
+ link
|
|
|
+ @click="removeGoods(form.rightFloors, fIndex, $index)"
|
|
|
+ >
|
|
|
+ 删除
|
|
|
+ </el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 适用设备信息(仅编辑模式显示) -->
|
|
|
+ <template v-if="isEdit && editDeviceId">
|
|
|
+ <el-divider content-position="left">适用设备</el-divider>
|
|
|
+ <div class="device-info">
|
|
|
+ <el-tag type="info" size="large">
|
|
|
+ 设备ID: {{ editDeviceId }}
|
|
|
+ </el-tag>
|
|
|
</div>
|
|
|
-
|
|
|
- <el-button
|
|
|
- type="primary"
|
|
|
- plain
|
|
|
- class="add-floor-btn"
|
|
|
- @click="addRightFloor"
|
|
|
- >
|
|
|
- + 添加右门层
|
|
|
- </el-button>
|
|
|
- </div>
|
|
|
+ </template>
|
|
|
</el-form>
|
|
|
|
|
|
<template #footer>
|
|
|
<el-button @click="handleClose">取消</el-button>
|
|
|
- <el-button type="primary" :loading="false" @click="handleSubmit">
|
|
|
- 确定
|
|
|
+ <el-button type="primary" @click="handleSubmit">
|
|
|
+ 保存
|
|
|
</el-button>
|
|
|
</template>
|
|
|
</el-dialog>
|
|
|
@@ -496,33 +673,21 @@ defineExpose({
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.template-form {
|
|
|
- max-height: 65vh;
|
|
|
+ max-height: 70vh;
|
|
|
overflow-y: auto;
|
|
|
padding-right: 10px;
|
|
|
|
|
|
- .floors-container {
|
|
|
+ .floor-toolbar {
|
|
|
+ margin-bottom: 12px;
|
|
|
display: flex;
|
|
|
- flex-direction: column;
|
|
|
- gap: 12px;
|
|
|
- margin-bottom: 16px;
|
|
|
-
|
|
|
- .floor-card {
|
|
|
- .card-header {
|
|
|
- display: flex;
|
|
|
- justify-content: space-between;
|
|
|
- align-items: center;
|
|
|
- font-weight: 500;
|
|
|
- }
|
|
|
-
|
|
|
- :deep(.el-card__body) {
|
|
|
- padding-top: 16px;
|
|
|
- }
|
|
|
- }
|
|
|
+ justify-content: flex-end;
|
|
|
+ }
|
|
|
|
|
|
- .add-floor-btn {
|
|
|
- width: 100%;
|
|
|
- border-style: dashed;
|
|
|
- }
|
|
|
+ .device-info {
|
|
|
+ display: flex;
|
|
|
+ gap: 8px;
|
|
|
+ align-items: center;
|
|
|
+ flex-wrap: wrap;
|
|
|
}
|
|
|
|
|
|
:deep(.el-divider__text) {
|
|
|
@@ -530,5 +695,17 @@ defineExpose({
|
|
|
font-weight: 600;
|
|
|
color: var(--el-color-primary);
|
|
|
}
|
|
|
+
|
|
|
+ :deep(.el-tabs__item) {
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-table) {
|
|
|
+ font-size: 13px;
|
|
|
+ }
|
|
|
+
|
|
|
+ :deep(.el-image) {
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|