feat: 支持商品参数隐藏配置
Build and Deploy Vue3 / build (push) Successful in 1m29s
Build and Deploy Vue3 / deploy (push) Successful in 55s

This commit is contained in:
shiran
2026-07-28 16:56:05 +08:00
parent c9e8f2ae15
commit b6f2b05ce7
2 changed files with 48 additions and 15 deletions
+13 -6
View File
@@ -102,7 +102,9 @@ export const getProductParameterList = (params) => {
}
/**创建商品参数 */
export const createProductParameter = (data) => {
return http2.post('/api/v1/admin/good/spec/create', data,{
const {good_id, ...payload} = data
return http2.post('/api/v1/admin/good/spec/create', payload,{
params: {good_id},
headers:{
'Content-Type':'multipart/form-data'
}
@@ -114,8 +116,9 @@ export const getProductParameterDetail = (params) => {
}
/**更新商品参数 */
export const updateProductParameter = (data) => {
return http2.post('/api/v1/admin/good/spec/update', null, {
params: data,
const {good_id, ...payload} = data
return http2.post('/api/v1/admin/good/spec/update', payload, {
params: {good_id},
headers:{
'Content-Type':'multipart/form-data'
}
@@ -129,7 +132,9 @@ export const deleteProductParameter = (data) => {
}
/**增加商品参数值 */
export const addProductParameterValue = (data) => {
return http2.post('/api/v1/admin/good/spec/add_value', data,{
const {good_id, ...payload} = data
return http2.post('/api/v1/admin/good/spec/add_value', payload,{
params: {good_id},
headers:{
'Content-Type':'multipart/form-data'
}
@@ -143,7 +148,9 @@ export const deleteProductParameterValue = (data) => {
}
/**更新商品参数值 */
export const updateProductParameterValue = (data) => {
return http2.post('/api/v1/admin/good/spec/update_value', data,{
const {good_id, ...payload} = data
return http2.post('/api/v1/admin/good/spec/update_value', payload,{
params: {good_id},
headers:{
'Content-Type':'multipart/form-data'
}
@@ -257,4 +264,4 @@ export const deleteProductGroupTag = (params) => {
/**获取用户已购商品列表 */
export const getUserGoodsList = (params) => {
return http2.get('/api/v1/admin/good/user_goods/list', {params: params})
}
}
@@ -37,6 +37,10 @@
</el-tag>
<span class="param-name" :title="param.name">{{ param.name }}</span>
<el-tag v-if="param.must" type="danger" size="small" effect="plain" class="title-must-tag">必选</el-tag>
<el-tag :type="param.hidden ? 'warning' : 'success'" size="small" effect="plain" class="title-visibility-tag">
<el-icon><component :is="param.hidden ? Hide : View" /></el-icon>
{{ param.hidden ? '用户端隐藏' : '用户端显示' }}
</el-tag>
<span class="param-id">#{{ param.id }}</span>
</div>
<div class="param-card-actions">
@@ -119,7 +123,10 @@
>
<el-icon class="value-drag-handle"><Rank /></el-icon>
<span class="value-order">#{{ idx }}</span>
<span class="value-name" :title="attr.name">{{ attr.name }}</span>
<div class="value-name-cell">
<span class="value-name" :title="attr.name">{{ attr.name }}</span>
<el-tag v-if="attr.hidden" size="small" type="warning" effect="plain" class="value-hidden-tag">用户端隐藏</el-tag>
</div>
<span class="value-range">
<el-tag v-if="param.type === 'number'" size="small" type="info">
{{ getRangeTypeText(attr.rangeType) }}
@@ -173,6 +180,10 @@
<el-form-item label="是否必选" prop="must">
<el-switch v-model="paramForm.must" :active-value="true" :inactive-value="false" active-text="必选" inactive-text="可选" />
</el-form-item>
<el-form-item label="对用户隐藏">
<el-switch v-model="paramForm.hidden" active-text="隐藏" inactive-text="显示" />
<div class="form-tip">隐藏后仅管理员可见用户端商品套餐及已购商品接口不会返回该参数</div>
</el-form-item>
</div>
<div class="tk-section">
<div class="tk-section-title">权限控制</div>
@@ -315,6 +326,10 @@
<el-form-item label="价格" prop="attr_price">
<el-input-number v-model="paramValueForm.attr_price" :min="0" placeholder="请输入价格" style="width: 100%" />
</el-form-item>
<el-form-item label="对用户隐藏">
<el-switch v-model="paramValueForm.hidden" active-text="隐藏" inactive-text="显示" />
<div class="form-tip">隐藏后该参数值不会出现在用户端商品及套餐配置中</div>
</el-form-item>
</div>
</el-form>
<template #footer>
@@ -332,7 +347,7 @@ import { ElMessage, ElMessageBox } from 'element-plus'
import {
Plus, Refresh, Rank, MagicStick,
Edit, Delete, Collection, List, Money,
ShoppingCart, User, Ticket
ShoppingCart, User, Ticket, Hide, View
} from '@element-plus/icons-vue'
import {
getProductParameterList,
@@ -369,6 +384,7 @@ const paramForm = reactive({
arg_name: '',
arg_type: 'string',
must: false,
hidden: false,
arg_step: 1,
arg_min: 0,
arg_max: 100,
@@ -450,7 +466,8 @@ const paramValueForm = reactive({
attr_range: 0,
attr_range_display: 0,
display_unit: '',
range_type: 'equal'
range_type: 'equal',
hidden: false
})
const paramValueRules = {
attr_name: [{ required: true, message: '请输入值名称', trigger: 'blur' }]
@@ -609,7 +626,8 @@ const reorderAttrs = async (param, src, dst) => {
attr_name: item.name,
attr_value: item.value || '',
attr_price: Number(item.price) / 100 || 0,
index: newIndex
index: newIndex,
hidden: item.hidden === true
}
if (param?.type === 'number') {
payload.attr_range = item.phase ?? 0
@@ -685,7 +703,7 @@ const handleAddParameter = () => {
paramFormType.value = 'add'
paramFormDialogVisible.value = true
Object.assign(paramForm, {
arg_id: undefined, arg_name: '', arg_type: 'string', must: false,
arg_id: undefined, arg_name: '', arg_type: 'string', must: false, hidden: false,
arg_step: 1, arg_min: 0, arg_max: 100,
step_display: 1, min_display: 0, max_display: 100,
step_unit: '', min_unit: '', max_unit: '',
@@ -714,7 +732,7 @@ const handleEditParameter = (row) => {
maxDisplay = fromBaseUnit(baseMax, defaultUnit, argKey)
}
Object.assign(paramForm, {
arg_id: row.id, arg_name: row.name, arg_type: row.type, must: row.must || false,
arg_id: row.id, arg_name: row.name, arg_type: row.type, must: row.must || false, hidden: row.hidden === true,
arg_step: baseStep, arg_min: baseMin, arg_max: baseMax,
step_display: stepDisplay, min_display: minDisplay, max_display: maxDisplay,
step_unit: defaultUnit, min_unit: defaultUnit, max_unit: defaultUnit,
@@ -745,6 +763,7 @@ const submitParamForm = () => {
arg_name: paramForm.arg_name,
arg_type: paramForm.arg_type,
must: paramForm.must === true,
hidden: paramForm.hidden === true,
user_add: paramForm.user_add === true,
use_user_group_discount: paramForm.use_user_group_discount === true,
use_user_discount: paramForm.use_user_discount === true
@@ -781,7 +800,7 @@ const handleAddParamValue = (param) => {
: 0
nextTick(() => {
paramValueFormRef.value?.resetFields()
Object.assign(paramValueForm, { attr_id: undefined, attr_name: '', attr_value: '', attr_price: 0, index: nextIndex, attr_range: 0, attr_range_display: 0, display_unit: defaultUnit, range_type: 'equal' })
Object.assign(paramValueForm, { attr_id: undefined, attr_name: '', attr_value: '', attr_price: 0, index: nextIndex, attr_range: 0, attr_range_display: 0, display_unit: defaultUnit, range_type: 'equal', hidden: false })
})
}
@@ -801,7 +820,7 @@ const handleEditParamValue = (row, param) => {
attr_id: row.id, attr_name: row.name, attr_value: row.value || '',
attr_price: row.price / 100 || 0, index: row.index || 0,
attr_range: baseValue, attr_range_display: displayValue,
display_unit: displayUnit, range_type: row.rangeType || 'equal'
display_unit: displayUnit, range_type: row.rangeType || 'equal', hidden: row.hidden === true
})
}
@@ -820,7 +839,7 @@ const submitParamValueForm = () => {
paramValueFormRef.value?.validate(async (valid) => {
if (valid) {
try {
const submitData = { good_id: Number(props.goodId), arg_id: Number(currentParam.value.id), attr_name: paramValueForm.attr_name, index: Number(paramValueForm.index), attr_price: Number(paramValueForm.attr_price) }
const submitData = { good_id: Number(props.goodId), arg_id: Number(currentParam.value.id), attr_name: paramValueForm.attr_name, index: Number(paramValueForm.index), attr_price: Number(paramValueForm.attr_price), hidden: paramValueForm.hidden === true }
if (currentParam.value.type === 'select') submitData.attr_value = paramValueForm.attr_value
if (currentParam.value.type === 'number') {
let rangeValue
@@ -933,6 +952,8 @@ watch(() => props.visible, (val) => {
}
.title-type-tag { flex-shrink: 0; }
.param-name {
flex: 1;
min-width: 0;
font-size: 16px;
font-weight: 600;
color: #303133;
@@ -941,6 +962,8 @@ watch(() => props.visible, (val) => {
text-overflow: ellipsis;
}
.title-must-tag { flex-shrink: 0; }
.title-visibility-tag { flex-shrink: 0; display: inline-flex !important; align-items: center; gap: 3px; }
.title-visibility-tag .el-icon { font-size: 12px; }
.param-id {
color: #909399;
font-size: 12px;
@@ -1081,14 +1104,17 @@ watch(() => props.visible, (val) => {
font-family: 'Monaco', 'Menlo', monospace;
text-align: center;
}
.value-name-cell { min-width: 0; display: flex; align-items: center; gap: 6px; }
.value-name {
color: #303133;
font-size: 13px;
font-weight: 500;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.value-hidden-tag { flex-shrink: 0; }
.value-range { min-width: 0; }
.value-raw {
color: #606266;