docs(readme): 更新文档说明
- 添加项目使用指南 - 完善API接口描述 - 修正错误的配置示例
This commit is contained in:
+587
-198
@@ -91,12 +91,16 @@
|
||||
>
|
||||
<el-table-column prop="name" label="名称" min-width="320">
|
||||
<template #default="{ row }">
|
||||
<div class="group-name-cell" :style="{ paddingLeft: row.isProduct ? ((row.level - 1) * 24 + 24) + 'px' : (row.level - 1) * 24 + 'px' }">
|
||||
<div
|
||||
class="group-name-cell"
|
||||
:class="{ 'is-clickable': row.isGroup || row.isProduct }"
|
||||
:style="{ paddingLeft: row.isProduct ? ((row.level - 1) * 24 + 24) + 'px' : (row.level - 1) * 24 + 'px' }"
|
||||
@click="handleNameCellClick(row)"
|
||||
>
|
||||
<!-- 分组的展开/收起按钮 -->
|
||||
<span
|
||||
v-if="row.isGroup"
|
||||
<span
|
||||
v-if="row.isGroup"
|
||||
class="expand-icon"
|
||||
@click="toggleExpand(row)"
|
||||
>
|
||||
<el-icon v-if="row._loading"><Loading /></el-icon>
|
||||
<el-icon v-else :class="{ 'is-expanded': row._expanded }"><ArrowRight /></el-icon>
|
||||
@@ -270,119 +274,134 @@
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogTitle"
|
||||
width="600px"
|
||||
width="860px"
|
||||
append-to-body
|
||||
class="product-form-dialog"
|
||||
>
|
||||
<el-form
|
||||
ref="groupFormRef"
|
||||
:model="groupForm"
|
||||
:rules="groupRules"
|
||||
label-width="100px"
|
||||
label-position="top"
|
||||
class="product-form"
|
||||
>
|
||||
<el-form-item label="分组名称" prop="name">
|
||||
<el-input v-model="groupForm.name" placeholder="请输入分组名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级分组" prop="parent_id">
|
||||
<div class="recommend-user-selector">
|
||||
<el-input
|
||||
:model-value="selectedParentName"
|
||||
:placeholder="isAddingChild ? '已自动设置父级分组' : '无父级(顶级分组)'"
|
||||
readonly
|
||||
:disabled="isAddingChild"
|
||||
@click="!isAddingChild && (showParentSelector = true)"
|
||||
<!-- 顶部:封面 + 基本信息 -->
|
||||
<div class="product-form-header">
|
||||
<!-- 左侧封面 -->
|
||||
<div class="cover-uploader" @click="showCoverSelector = true">
|
||||
<img v-if="groupForm.cover_url" :src="groupForm.cover_url" class="cover-image" alt="分组封面" />
|
||||
<div v-else class="cover-placeholder">
|
||||
<el-icon class="cover-placeholder-icon"><Folder /></el-icon>
|
||||
<span class="cover-placeholder-text">点击选择封面</span>
|
||||
</div>
|
||||
<div class="cover-mask">
|
||||
<el-icon><Picture /></el-icon>
|
||||
<span>更换封面</span>
|
||||
</div>
|
||||
<el-button
|
||||
v-if="groupForm.cover_id"
|
||||
type="danger"
|
||||
size="small"
|
||||
circle
|
||||
class="cover-clear-btn"
|
||||
@click.stop="clearGroupCover"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="!isAddingChild && (showParentSelector = true)" :disabled="isAddingChild">
|
||||
<el-icon><Search /></el-icon>
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
v-if="groupForm.parent_id && !isAddingChild"
|
||||
type="danger"
|
||||
link
|
||||
@click="clearParent"
|
||||
class="clear-btn"
|
||||
>
|
||||
清除
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-if="isAddingChild" class="form-tip">
|
||||
添加子级时自动继承父级分组,不可修改
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组层级" prop="level">
|
||||
<el-input :model-value="`${groupForm.level}级`" disabled style="width: 100%" />
|
||||
<div class="form-tip">层级根据父级分组自动计算</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组封面" prop="cover_id">
|
||||
<div class="recommend-user-selector">
|
||||
<el-input
|
||||
:model-value="groupForm.cover_id ? `文件ID: ${groupForm.cover_id}` : ''"
|
||||
placeholder="点击选择封面图片"
|
||||
readonly
|
||||
@click="showCoverSelector = true"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="showCoverSelector = true">
|
||||
<el-icon><Search /></el-icon>
|
||||
|
||||
<!-- 右侧基本信息 -->
|
||||
<div class="basic-fields">
|
||||
<el-form-item label="分组名称" prop="name">
|
||||
<el-input v-model="groupForm.name" placeholder="请输入分组名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="父级分组" prop="parent_id">
|
||||
<div
|
||||
class="group-picker"
|
||||
:class="{ 'is-empty': !selectedParentName, 'is-disabled': isAddingChild }"
|
||||
@click="!isAddingChild && (showParentSelector = true)"
|
||||
>
|
||||
<el-icon class="group-picker-icon"><Folder /></el-icon>
|
||||
<span class="group-picker-text">
|
||||
{{ selectedParentName || (isAddingChild ? '已自动设置父级分组' : '无父级(顶级分组)') }}
|
||||
</span>
|
||||
<el-button
|
||||
v-if="groupForm.parent_id && !isAddingChild"
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
class="group-picker-clear"
|
||||
@click.stop="clearParent"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
v-if="groupForm.cover_id"
|
||||
type="danger"
|
||||
link
|
||||
@click="groupForm.cover_id = undefined"
|
||||
class="clear-btn"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组标签" prop="tag_id">
|
||||
<div class="recommend-user-selector">
|
||||
<el-input
|
||||
:model-value="selectedTagName"
|
||||
placeholder="点击选择分组标签"
|
||||
readonly
|
||||
@click="showTagSelector = true"
|
||||
>
|
||||
<template #append>
|
||||
<el-button @click="showTagSelector = true">
|
||||
<el-icon><Search /></el-icon>
|
||||
<el-icon v-if="!isAddingChild" class="group-picker-arrow"><ArrowRight /></el-icon>
|
||||
</div>
|
||||
<div v-if="isAddingChild" class="form-tip">
|
||||
添加子级时自动继承父级分组,不可修改
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分组标签" prop="tag_id">
|
||||
<div
|
||||
class="group-picker"
|
||||
:class="{ 'is-empty': !selectedTagName }"
|
||||
@click="showTagSelector = true"
|
||||
>
|
||||
<el-icon class="group-picker-icon"><CollectionTag /></el-icon>
|
||||
<span class="group-picker-text">{{ selectedTagName || '点击选择分组标签' }}</span>
|
||||
<el-button
|
||||
v-if="groupForm.tag_id"
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
class="group-picker-clear"
|
||||
@click.stop="clearTag"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
v-if="groupForm.tag_id"
|
||||
type="danger"
|
||||
link
|
||||
@click="clearTag"
|
||||
class="clear-btn"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
<el-icon class="group-picker-arrow"><ArrowRight /></el-icon>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="note">
|
||||
<el-input v-model="groupForm.note" type="textarea" :rows="4" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序索引" prop="index">
|
||||
<el-input-number
|
||||
v-model="groupForm.index"
|
||||
:min="0"
|
||||
:max="9999"
|
||||
placeholder="请输入排序索引,数值越小排序越靠前"
|
||||
style="width: 100%"
|
||||
</div>
|
||||
|
||||
<!-- 基本属性(层级 / 排序 / 状态) -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">基本属性</div>
|
||||
<div class="form-grid">
|
||||
<el-form-item label="分组层级" prop="level">
|
||||
<el-input :model-value="`${groupForm.level} 级`" disabled style="width: 100%" />
|
||||
<div class="form-tip">层级根据父级分组自动计算</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序索引" prop="index">
|
||||
<el-input-number
|
||||
v-model="groupForm.index"
|
||||
:min="0"
|
||||
:max="9999"
|
||||
controls-position="right"
|
||||
placeholder="数值越小越靠前"
|
||||
style="width: 100%"
|
||||
/>
|
||||
<div class="form-tip">数值越小排序越靠前,相同索引按创建时间排序</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="disable" class="group-status-item">
|
||||
<el-radio-group v-model="groupForm.disable">
|
||||
<el-radio-button :value="false">启用</el-radio-button>
|
||||
<el-radio-button :value="true">禁用</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 备注 -->
|
||||
<el-form-item prop="note" label="备注" class="note-form-item">
|
||||
<el-input
|
||||
v-model="groupForm.note"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入备注(可选)"
|
||||
resize="none"
|
||||
/>
|
||||
<div class="form-tip">数值越小排序越靠前,相同索引按创建时间排序</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="disable">
|
||||
<el-radio-group v-model="groupForm.disable">
|
||||
<el-radio :value="false">启用</el-radio>
|
||||
<el-radio :value="true">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -486,71 +505,80 @@
|
||||
<el-dialog
|
||||
v-model="productDialogVisible"
|
||||
:title="productDialogType === 'add' ? '新增商品' : '编辑商品'"
|
||||
width="800px"
|
||||
width="860px"
|
||||
append-to-body
|
||||
class="product-form-dialog"
|
||||
>
|
||||
<el-form
|
||||
ref="productFormRef"
|
||||
:model="productForm"
|
||||
:rules="productRules"
|
||||
label-width="120px"
|
||||
label-position="top"
|
||||
class="product-form"
|
||||
>
|
||||
<el-form-item label="商品名称" prop="name">
|
||||
<el-input v-model="productForm.name" placeholder="请输入商品名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品所属表" prop="table">
|
||||
<el-input v-model="productForm.table" placeholder="请输入商品所属表" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品分组" prop="good_group_id">
|
||||
<div class="recommend-user-selector">
|
||||
<el-input
|
||||
:model-value="selectedGroupName"
|
||||
placeholder="请选择商品分组"
|
||||
readonly
|
||||
@click="showProductGroupSelector = true"
|
||||
<!-- 顶部:封面 + 基本信息 -->
|
||||
<div class="product-form-header">
|
||||
<!-- 左侧封面 -->
|
||||
<div class="cover-uploader" @click="coverSelectorVisible = true">
|
||||
<img v-if="productForm.cover_url" :src="productForm.cover_url" class="cover-image" alt="商品封面" />
|
||||
<div v-else class="cover-placeholder">
|
||||
<el-icon class="cover-placeholder-icon"><Picture /></el-icon>
|
||||
<span class="cover-placeholder-text">点击选择封面</span>
|
||||
</div>
|
||||
<div class="cover-mask">
|
||||
<el-icon><Picture /></el-icon>
|
||||
<span>更换封面</span>
|
||||
</div>
|
||||
<el-button
|
||||
v-if="productForm.cover_id"
|
||||
type="danger"
|
||||
size="small"
|
||||
circle
|
||||
class="cover-clear-btn"
|
||||
@click.stop="clearProductCover"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Folder /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
v-if="productForm.good_group_id"
|
||||
type="danger"
|
||||
link
|
||||
@click="clearProductGroup"
|
||||
class="clear-btn"
|
||||
>
|
||||
清除
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品内容" prop="content">
|
||||
<el-input v-model="productForm.content" type="textarea" :rows="4" placeholder="请输入商品内容" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品封面" prop="cover_id">
|
||||
<div class="recommend-user-selector">
|
||||
<el-input
|
||||
:model-value="productForm.cover_id ? `文件ID: ${productForm.cover_id}` : ''"
|
||||
placeholder="点击选择封面图片"
|
||||
readonly
|
||||
@click="coverSelectorVisible = true"
|
||||
>
|
||||
<template #prefix>
|
||||
<el-icon><Picture /></el-icon>
|
||||
</template>
|
||||
</el-input>
|
||||
<el-button
|
||||
v-if="productForm.cover_id"
|
||||
type="danger"
|
||||
link
|
||||
@click="clearProductCover"
|
||||
class="clear-btn"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
|
||||
<!-- 右侧基本信息 -->
|
||||
<div class="basic-fields">
|
||||
<el-form-item label="商品名称" prop="name">
|
||||
<el-input v-model="productForm.name" placeholder="请输入商品名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="所属分组" prop="good_group_id">
|
||||
<div
|
||||
class="group-picker"
|
||||
:class="{ 'is-empty': !selectedGroupName }"
|
||||
@click="showProductGroupSelector = true"
|
||||
>
|
||||
<el-icon class="group-picker-icon"><Folder /></el-icon>
|
||||
<span class="group-picker-text">{{ selectedGroupName || '点击选择商品分组' }}</span>
|
||||
<el-button
|
||||
v-if="productForm.good_group_id"
|
||||
type="danger"
|
||||
link
|
||||
size="small"
|
||||
class="group-picker-clear"
|
||||
@click.stop="clearProductGroup"
|
||||
>
|
||||
清除
|
||||
</el-button>
|
||||
<el-icon class="group-picker-arrow"><ArrowRight /></el-icon>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="商品介绍" prop="content">
|
||||
<el-input
|
||||
v-model="productForm.content"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
placeholder="请输入商品介绍"
|
||||
resize="none"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- 封面选择器弹窗 -->
|
||||
<AvatarSelector
|
||||
v-model="coverSelectorVisible"
|
||||
@@ -559,45 +587,119 @@
|
||||
title="选择封面"
|
||||
@confirm="handleProductCoverSelect"
|
||||
/>
|
||||
|
||||
<el-form-item label="库存控制" prop="inventory_control">
|
||||
<el-switch v-model="productForm.inventory_control" active-text="启用" inactive-text="禁用" />
|
||||
</el-form-item>
|
||||
<el-form-item label="库存数量" prop="inventory">
|
||||
<el-input-number v-model="productForm.inventory" :min="0" placeholder="请输入库存" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="商品价格" prop="price">
|
||||
<div class="unit-input-row">
|
||||
<el-input-number v-model="productForm.price" :min="0" :precision="2" :step="0.01" placeholder="请输入价格(元)" style="flex:1" />
|
||||
<span class="unit-text">元</span>
|
||||
|
||||
<!-- 价格与销售 -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">价格与销售</div>
|
||||
<div class="form-grid">
|
||||
<el-form-item prop="price">
|
||||
<template #label>
|
||||
<span>商品价格<span class="unit-suffix">(元)</span></span>
|
||||
</template>
|
||||
<el-input-number
|
||||
v-model="productForm.price"
|
||||
:min="0"
|
||||
:precision="2"
|
||||
:step="0.01"
|
||||
placeholder="请输入价格"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="单个商品数量" prop="pay_num">
|
||||
<el-input-number
|
||||
v-model="productForm.pay_num"
|
||||
:min="1"
|
||||
placeholder="请输入数量"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="expire_time">
|
||||
<template #label>
|
||||
<span>有效期<span class="unit-suffix">(天,0 为永久)</span></span>
|
||||
</template>
|
||||
<el-input-number
|
||||
v-model="productForm.expire_time"
|
||||
:min="0"
|
||||
placeholder="请输入有效期"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="允许续费" prop="can_renew">
|
||||
<el-switch
|
||||
v-model="productForm.can_renew"
|
||||
active-text="允许"
|
||||
inactive-text="禁止"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="单个商品数量" prop="pay_num">
|
||||
<el-input-number v-model="productForm.pay_num" :min="1" placeholder="请输入单个商品数量" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="expire_time">
|
||||
<div class="unit-input-row">
|
||||
<el-input-number v-model="productForm.expire_time" :min="0" placeholder="请输入有效期" style="flex:1" />
|
||||
<span class="unit-text">天</span>
|
||||
</div>
|
||||
|
||||
<!-- 库存管理 -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">库存管理</div>
|
||||
<div class="form-grid">
|
||||
<el-form-item label="库存控制" prop="inventory_control">
|
||||
<el-switch
|
||||
v-model="productForm.inventory_control"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="库存数量" prop="inventory">
|
||||
<el-input-number
|
||||
v-model="productForm.inventory"
|
||||
:min="0"
|
||||
:disabled="!productForm.inventory_control"
|
||||
placeholder="请输入库存"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="推荐" prop="recommend">
|
||||
<el-switch v-model="productForm.recommend" active-text="启用" inactive-text="禁用" />
|
||||
</el-form-item>
|
||||
<el-form-item label="推荐返还(%)" prop="recommend_rebate">
|
||||
<el-input-number v-model="productForm.recommend_rebate" :min="0" :max="100" placeholder="请输入返还百分比" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item label="参数类型" prop="arg_type">
|
||||
<el-select v-model="productForm.arg_type" placeholder="请选择参数类型" style="width: 100%">
|
||||
<el-option label="所有参数" value="all" />
|
||||
<el-option label="套餐" value="plan" />
|
||||
<el-option label="自定义" value="customize" />
|
||||
</el-select>
|
||||
<div class="form-tip">all: 所有参数 / plan: 套餐 / customize: 自定义</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="允许续费" prop="can_renew">
|
||||
<el-switch v-model="productForm.can_renew" active-text="允许" inactive-text="禁止" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 推荐与参数 -->
|
||||
<div class="form-section">
|
||||
<div class="form-section-title">推荐与参数</div>
|
||||
<div class="form-grid">
|
||||
<el-form-item label="推荐" prop="recommend">
|
||||
<el-switch
|
||||
v-model="productForm.recommend"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="recommend_rebate">
|
||||
<template #label>
|
||||
<span>推荐返还<span class="unit-suffix">(%)</span></span>
|
||||
</template>
|
||||
<el-input-number
|
||||
v-model="productForm.recommend_rebate"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:disabled="!productForm.recommend"
|
||||
placeholder="返还百分比"
|
||||
controls-position="right"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="参数类型" prop="arg_type" class="arg-type-item">
|
||||
<el-select
|
||||
v-model="productForm.arg_type"
|
||||
placeholder="请选择参数类型"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option label="所有参数" value="all" />
|
||||
<el-option label="套餐" value="plan" />
|
||||
<el-option label="自定义" value="customize" />
|
||||
</el-select>
|
||||
<div class="form-tip">all: 所有参数 / plan: 套餐 / customize: 自定义</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -657,7 +759,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted, watch } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { Plus, Refresh, Search, Folder, ArrowRight, Loading, Grid, List, Document, Picture } from '@element-plus/icons-vue'
|
||||
import { Plus, Refresh, Search, Folder, ArrowRight, Loading, Grid, List, Document, Picture, Delete, CollectionTag } from '@element-plus/icons-vue'
|
||||
import {
|
||||
getProductGroupList,
|
||||
createProductGroup,
|
||||
@@ -712,6 +814,7 @@ const groupForm = reactive({
|
||||
level: 1,
|
||||
parent_id: undefined,
|
||||
cover_id: undefined,
|
||||
cover_url: '',
|
||||
tag_id: undefined,
|
||||
index: 0
|
||||
})
|
||||
@@ -752,6 +855,7 @@ const productForm = reactive({
|
||||
tag: '',
|
||||
content: '',
|
||||
cover_id: undefined,
|
||||
cover_url: '',
|
||||
good_group_id: undefined,
|
||||
inventory_control: false,
|
||||
inventory: 0,
|
||||
@@ -768,9 +872,6 @@ const productRules = {
|
||||
name: [
|
||||
{ required: true, message: '请输入商品名称', trigger: 'blur' }
|
||||
],
|
||||
table: [
|
||||
{ required: true, message: '请输入商品所属表', trigger: 'blur' }
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: '请输入商品内容', trigger: 'blur' }
|
||||
],
|
||||
@@ -928,6 +1029,14 @@ const loadProductsForGroup = async (groupId) => {
|
||||
}
|
||||
}
|
||||
|
||||
const handleNameCellClick = (row) => {
|
||||
if (row.isGroup) {
|
||||
toggleExpand(row)
|
||||
} else if (row.isProduct) {
|
||||
handleEditProduct(row.data, row.parentId)
|
||||
}
|
||||
}
|
||||
|
||||
const toggleExpand = async (row) => {
|
||||
const group = row._origRef || findGroupById(row.id)
|
||||
if (!group || group._loading) return
|
||||
@@ -1300,6 +1409,7 @@ const handleAdd = (parentRow) => {
|
||||
level: parentRow.level + 1,
|
||||
parent_id: parentRow.id,
|
||||
cover_id: undefined,
|
||||
cover_url: '',
|
||||
tag_id: parentTagId || undefined,
|
||||
index: 0
|
||||
})
|
||||
@@ -1314,6 +1424,7 @@ const handleAdd = (parentRow) => {
|
||||
level: 1,
|
||||
parent_id: undefined,
|
||||
cover_id: undefined,
|
||||
cover_url: '',
|
||||
tag_id: undefined,
|
||||
index: 0
|
||||
})
|
||||
@@ -1334,6 +1445,7 @@ const handleEdit = (row) => {
|
||||
level: row.level || 1,
|
||||
parent_id: row.parentId || undefined,
|
||||
cover_id: row.coverId || undefined,
|
||||
cover_url: row.cover || '',
|
||||
tag_id: row.tag?.id || row.tagId || undefined,
|
||||
index: row.index || 0
|
||||
})
|
||||
@@ -1393,6 +1505,7 @@ const handleAddProduct = () => {
|
||||
tag: '',
|
||||
content: '',
|
||||
cover_id: undefined,
|
||||
cover_url: '',
|
||||
good_group_id: undefined,
|
||||
inventory_control: false,
|
||||
inventory: 0,
|
||||
@@ -1424,6 +1537,7 @@ const handleEditProduct = (product, parentGroupId) => {
|
||||
tag: typeof product.tag === 'string' ? product.tag : '',
|
||||
content: product.content || '',
|
||||
cover_id: product.coverId,
|
||||
cover_url: product.cover || '',
|
||||
good_group_id: groupId,
|
||||
inventory_control: product.inventoryControl,
|
||||
inventory: product.inventory,
|
||||
@@ -1506,11 +1620,13 @@ const clearProductGroup = () => {
|
||||
|
||||
const handleProductCoverSelect = (file) => {
|
||||
productForm.cover_id = file.cover_id
|
||||
productForm.cover_url = file.url || ''
|
||||
coverSelectorVisible.value = false
|
||||
}
|
||||
|
||||
const clearProductCover = () => {
|
||||
productForm.cover_id = undefined
|
||||
productForm.cover_url = ''
|
||||
}
|
||||
|
||||
const clearParent = () => {
|
||||
@@ -1533,6 +1649,12 @@ const confirmParentSelect = () => {
|
||||
|
||||
const handleCoverSelect = (file) => {
|
||||
groupForm.cover_id = file.cover_id
|
||||
groupForm.cover_url = file.url || ''
|
||||
}
|
||||
|
||||
const clearGroupCover = () => {
|
||||
groupForm.cover_id = undefined
|
||||
groupForm.cover_url = ''
|
||||
}
|
||||
|
||||
const handleStatusChange = async (row, disable) => {
|
||||
@@ -1755,6 +1877,21 @@ onMounted(() => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 4px;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.group-name-cell.is-clickable {
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.group-name-cell.is-clickable:hover {
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
.group-name-cell.is-clickable:hover .expand-icon {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.group-name {
|
||||
@@ -1847,6 +1984,258 @@ onMounted(() => {
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
/* 商品表单优化布局 */
|
||||
.product-form-dialog :deep(.el-dialog__body) {
|
||||
padding-top: 16px;
|
||||
}
|
||||
|
||||
.product-form {
|
||||
--section-gap: 20px;
|
||||
}
|
||||
|
||||
.product-form-header {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
padding: 16px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
margin-bottom: var(--section-gap);
|
||||
}
|
||||
|
||||
.cover-uploader {
|
||||
position: relative;
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
flex-shrink: 0;
|
||||
border: 1px dashed #dcdfe6;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
background: #fff;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
|
||||
.cover-uploader:hover {
|
||||
border-color: #409eff;
|
||||
}
|
||||
|
||||
.cover-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.cover-placeholder {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.cover-placeholder-icon {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.cover-placeholder-text {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.cover-mask {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 6px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.cover-mask .el-icon {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.cover-uploader:hover .cover-mask {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.cover-clear-btn {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 2;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.cover-uploader:hover .cover-clear-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.basic-fields {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.basic-fields :deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.basic-fields :deep(.el-form-item:last-child) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.group-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
padding: 0 11px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
transition: border-color 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.group-picker:hover {
|
||||
border-color: #c0c4cc;
|
||||
}
|
||||
|
||||
.group-picker.is-empty .group-picker-text {
|
||||
color: #a8abb2;
|
||||
}
|
||||
|
||||
.group-picker.is-disabled {
|
||||
background: #f5f7fa;
|
||||
cursor: not-allowed;
|
||||
color: #a8abb2;
|
||||
}
|
||||
|
||||
.group-picker.is-disabled:hover {
|
||||
border-color: #dcdfe6;
|
||||
}
|
||||
|
||||
.group-picker.is-disabled .group-picker-text,
|
||||
.group-picker.is-disabled .group-picker-icon {
|
||||
color: #a8abb2;
|
||||
}
|
||||
|
||||
.note-form-item {
|
||||
margin-bottom: 0 !important;
|
||||
padding: 16px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.group-status-item :deep(.el-radio-button__inner) {
|
||||
padding: 6px 18px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.group-picker-icon {
|
||||
color: #909399;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.group-picker-text {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.group-picker-clear {
|
||||
flex-shrink: 0;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.group-picker-arrow {
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
margin-bottom: var(--section-gap);
|
||||
padding: 16px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.form-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.form-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 12px;
|
||||
padding-left: 8px;
|
||||
border-left: 3px solid #409eff;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.form-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 4px 24px;
|
||||
}
|
||||
|
||||
.form-grid :deep(.el-form-item) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.form-grid .arg-type-item {
|
||||
grid-column: span 2;
|
||||
}
|
||||
|
||||
.unit-suffix {
|
||||
color: #909399;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.product-form :deep(.el-form-item__label) {
|
||||
font-weight: 500;
|
||||
padding-bottom: 4px !important;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@media (max-width: 720px) {
|
||||
.product-form-header {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.form-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.form-grid .arg-type-item {
|
||||
grid-column: span 1;
|
||||
}
|
||||
}
|
||||
|
||||
.product-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
Reference in New Issue
Block a user