修改bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 8m39s

This commit is contained in:
sexygoat
2026-03-11 17:09:35 +08:00
parent bd45f7a224
commit d43de4cd06
37 changed files with 2552 additions and 1696 deletions

View File

@@ -34,15 +34,21 @@
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 右键菜单 -->
<ArtMenuRight
ref="contextMenuRef"
@@ -251,8 +257,10 @@
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import { RoutesAlias } from '@/router/routesAlias'
@@ -283,6 +291,15 @@
const seriesOptions = ref<SeriesSelectOption[]>([])
const searchSeriesOptions = ref<SeriesSelectOption[]>([])
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
// 搜索表单初始值
const initialSearchState = {
package_name: '',
@@ -952,6 +969,10 @@
</script>
<style lang="scss" scoped>
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
.package-list-page {
// 可以添加特定样式
}

View File

@@ -35,15 +35,21 @@
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 套餐系列操作右键菜单 -->
<ArtMenuRight
ref="seriesOperationMenuRef"
@@ -245,7 +251,7 @@
style="width: 100%"
>
<ElOption label="仅自己" value="self" />
<ElOption label="自己+下级" value="self_and_sub" />
<!--<ElOption label="自己+下级" value="self_and_sub" />-->
</ElSelect>
</div>
</div>
@@ -396,8 +402,10 @@
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -1112,6 +1120,15 @@
ElMessage.warning('您没有查看详情的权限')
}
}
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
</script>
<style lang="scss" scoped>
@@ -1122,4 +1139,8 @@
.dialog-footer {
text-align: right;
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
</style>

View File

@@ -91,13 +91,14 @@
<ElTableColumn label="统计范围" width="120">
<template #default="{ row }">
<ElTag size="small" type="warning">
{{
row.stat_scope === 'self'
? '仅自己'
: row.stat_scope === 'self_and_sub'
? '自己+下级'
: '-'
}}
仅自己
<!--{{-->
<!-- row.stat_scope === 'self'-->
<!-- ? '仅自己'-->
<!-- : row.stat_scope === 'self_and_sub'-->
<!-- ? '自己+下级'-->
<!-- : '-'-->
<!--}}-->
</ElTag>
</template>
</ElTableColumn>

View File

@@ -35,15 +35,21 @@
:pageSize="pagination.page_size"
:total="pagination.total"
:marginTop="10"
:row-class-name="getRowClassName"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
@row-contextmenu="handleRowContextMenu"
@cell-mouse-enter="handleCellMouseEnter"
@cell-mouse-leave="handleCellMouseLeave"
>
<template #default>
<ElTableColumn v-for="col in columns" :key="col.prop || col.type" v-bind="col" />
</template>
</ArtTable>
<!-- 鼠标悬浮提示 -->
<TableContextMenuHint :visible="showContextMenuHint" :position="hintPosition" />
<!-- 右键菜单 -->
<ArtMenuRight
ref="contextMenuRef"
@@ -52,60 +58,443 @@
@select="handleContextMenuSelect"
/>
<!-- 套餐列表对话框 -->
<ElDialog
v-model="packageListDialogVisible"
title="套餐列表"
width="65%"
:close-on-click-modal="false"
@closed="handlePackageListDialogClosed"
>
<div class="package-list-dialog-content">
<!-- 添加授权套餐按钮 -->
<div class="package-list-header">
<ElButton
type="primary"
@click="showAddPackageDialog"
v-permission="'series_grants:manage_packages'"
>
添加授权套餐
</ElButton>
</div>
<!-- 套餐列表 -->
<ElTable
v-if="currentGrantPackages.length > 0"
:data="currentGrantPackages"
border
stripe
style="margin-top: 12px"
>
<ElTableColumn prop="package_name" label="套餐名称" />
<ElTableColumn prop="package_code" label="套餐编码" />
<ElTableColumn label="成本价">
<template #default="{ row }">
<span class="amount-value">¥{{ (row.cost_price / 100).toFixed(2) }}</span>
</template>
</ElTableColumn>
<ElTableColumn label="上架状态" align="center">
<template #default="{ row }">
<ElTag v-if="row.shelf_status === 1" type="success" size="small">上架</ElTag>
<ElTag v-else-if="row.shelf_status === 2" type="info" size="small">下架</ElTag>
<span v-else>-</span>
</template>
</ElTableColumn>
<ElTableColumn label="状态" width="100" align="center">
<template #default="{ row }">
<ElTag v-if="row.status === 1" type="success" size="small">启用</ElTag>
<ElTag v-else-if="row.status === 2" type="danger" size="small">禁用</ElTag>
<span v-else>-</span>
</template>
</ElTableColumn>
<ElTableColumn label="操作" width="150" align="center" fixed="right">
<template #default="{ row }">
<ElButton
type="primary"
size="small"
link
@click="showEditPackageDialog(row)"
v-permission="'series_grants:edit_packages'"
>
编辑
</ElButton>
<ElButton
type="danger"
size="small"
link
@click="handleDeletePackage(row)"
v-permission="'series_grants:delete_packages'"
>
删除
</ElButton>
</template>
</ElTableColumn>
</ElTable>
<ElEmpty v-else description="暂无套餐" :image-size="80" />
</div>
</ElDialog>
<!-- 添加/编辑套餐对话框 -->
<ElDialog
v-model="packageDialogVisible"
:title="packageDialogType === 'add' ? '添加套餐' : '编辑套餐'"
width="500px"
:close-on-click-modal="false"
@closed="handlePackageDialogClosed"
>
<ElForm
ref="packageFormRef"
:model="packageForm"
:rules="packageFormRules"
label-width="100px"
>
<!-- 添加模式选择套餐 -->
<ElFormItem label="选择套餐" prop="package_id" v-if="packageDialogType === 'add'">
<ElSelect
v-model="packageForm.package_id"
placeholder="请选择套餐"
style="width: 100%"
filterable
remote
:remote-method="searchAvailablePackages"
:loading="packageLoading"
clearable
>
<template
v-if="availablePackages.length === 0 && !packageLoading && currentGrantSeriesId"
>
<ElOption disabled value="" label="该系列没有可选套餐" />
</template>
<ElOption
v-for="pkg in availablePackages"
:key="pkg.id"
:label="`${pkg.package_name} (${pkg.package_code})`"
:value="pkg.id"
:disabled="isPackageAlreadyAdded(pkg.id)"
/>
</ElSelect>
</ElFormItem>
<!-- 编辑模式显示套餐信息 -->
<ElFormItem label="套餐名称" v-if="packageDialogType === 'edit'">
<span>{{ packageForm.package_name }}</span>
</ElFormItem>
<ElFormItem label="套餐编码" v-if="packageDialogType === 'edit'">
<span>{{ packageForm.package_code }}</span>
</ElFormItem>
<!-- 成本价 -->
<ElFormItem label="成本价(元)" prop="cost_price_yuan">
<ElInputNumber
v-model="packageForm.cost_price_yuan"
:min="0"
:precision="2"
:step="0.01"
:controls="false"
style="width: 100%"
placeholder="请输入成本价"
/>
</ElFormItem>
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="packageDialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSavePackage" :loading="packageSubmitLoading">
保存
</ElButton>
</div>
</template>
</ElDialog>
<!-- 新增/编辑对话框 -->
<ElDialog
v-model="dialogVisible"
:title="dialogType === 'add' ? '新增代理系列授权' : '编辑代理系列授权'"
width="50%"
width="60%"
:close-on-click-modal="false"
@closed="handleDialogClosed"
>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="130px">
<!-- 新增模式基本信息 - 2列布局 -->
<div v-if="dialogType === 'add'">
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="选择套餐系列" prop="series_id">
<!-- 新增模式分步骤表单 -->
<template v-if="dialogType === 'add'">
<!-- 步骤条 -->
<ElSteps
:active="currentStep"
finish-status="success"
align-center
style="margin-bottom: 30px"
>
<ElStep title="基本信息" description="选择系列和店铺" />
<ElStep title="佣金和充值配置" description="设置佣金和强制充值" />
</ElSteps>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="130px">
<!-- 第一步基本信息 -->
<div v-show="currentStep === 0">
<ElRow :gutter="20">
<ElCol :span="24">
<ElFormItem label="选择套餐系列" prop="series_id">
<ElSelect
v-model="form.series_id"
placeholder="请选择套餐系列"
style="width: 100%"
filterable
remote
:remote-method="searchSeries"
:loading="seriesLoading"
clearable
>
<ElOption
v-for="series in seriesOptions"
:key="series.id"
:label="`${series.series_name} (${series.series_code})`"
:value="series.id"
/>
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
<ElRow :gutter="20" v-if="form.series_id">
<ElCol :span="24">
<ElFormItem label="选择店铺" prop="shop_id">
<ElTreeSelect
v-model="form.shop_id"
:data="shopTreeData"
:props="{ label: 'shop_name', value: 'id', children: 'children' }"
placeholder="请选择店铺"
style="width: 100%"
filterable
clearable
:loading="shopLoading"
check-strictly
:render-after-expand="false"
/>
</ElFormItem>
</ElCol>
</ElRow>
<!-- 套餐配置 -->
<div v-if="form.shop_id" class="form-section-title">
<span class="title-text">套餐配置可选</span>
</div>
<template v-if="form.shop_id">
<!-- 选择套餐 -->
<ElFormItem label="选择套餐">
<ElSelect
v-model="form.series_id"
placeholder="请选择套餐系列"
v-model="selectedPackageIds"
placeholder="请选择套餐"
style="width: 100%"
multiple
filterable
remote
:remote-method="searchSeries"
:loading="seriesLoading"
:remote-method="searchPackages"
:loading="packageLoading"
clearable
>
<template
v-if="packageOptions.length === 0 && !packageLoading && form.series_id"
>
<ElOption disabled value="" label="该系列没有可选套餐" />
</template>
<ElOption
v-for="series in seriesOptions"
:key="series.id"
:label="`${series.series_name} (${series.series_code})`"
:value="series.id"
v-for="pkg in packageOptions"
:key="pkg.id"
:label="pkg.package_name"
:value="pkg.id"
/>
</ElSelect>
<div class="form-tip">选择该授权下包含的套餐</div>
</ElFormItem>
</ElCol>
<ElCol :span="12">
<ElFormItem label="选择店铺" prop="shop_id">
<ElTreeSelect
v-model="form.shop_id"
:data="shopTreeData"
:props="{ label: 'shop_name', value: 'id', children: 'children' }"
placeholder="请选择店铺"
style="width: 100%"
filterable
clearable
:loading="shopLoading"
check-strictly
:render-after-expand="false"
/>
</ElFormItem>
</ElCol>
</ElRow>
</div>
<!-- 套餐成本价 -->
<ElFormItem label="套餐成本价" v-if="form.packages.length > 0">
<div class="package-list">
<div
v-for="(pkg, index) in form.packages"
:key="pkg.package_id"
class="package-item"
>
<span class="package-name">{{ getPackageName(pkg.package_id) }}</span>
<div class="cost-price-input-wrapper">
<ElInputNumber
v-model="pkg.cost_price"
:min="pkg.original_cost_price || 0"
:precision="2"
:step="0.01"
:controls="false"
placeholder="成本价(元)"
style="width: 150px"
/>
<span v-if="pkg.original_cost_price" class="min-cost-hint">
(成本价: ¥{{ pkg.original_cost_price.toFixed(2) }})
</span>
</div>
<ElButton type="danger" size="small" @click="removePackage(index)"
>删除</ElButton
>
</div>
</div>
<div class="form-tip"
>设置每个套餐的成本价单位不能低于套餐原始成本价</div
>
</ElFormItem>
</template>
</div>
<!-- 第二步佣金和强制充值配置 -->
<div v-show="currentStep === 1">
<!-- 一次性佣金配置 -->
<div class="form-section-title">
<span class="title-text">一次性佣金配置</span>
</div>
<!-- 佣金类型和金额 - 2列布局 -->
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="佣金类型">
<div class="commission-type-display">
<ElTag
:type="form.commission_type === 'fixed' ? 'success' : 'warning'"
size="large"
>
{{ form.commission_type === 'fixed' ? '固定佣金' : '梯度佣金' }}
</ElTag>
<span class="type-hint">从套餐系列配置继承</span>
</div>
</ElFormItem>
</ElCol>
<ElCol :span="12" v-if="form.commission_type === 'fixed'">
<ElFormItem label="佣金金额(元)" prop="one_time_commission_amount">
<ElInputNumber
v-model="form.one_time_commission_amount"
:min="0"
:max="form.series_max_commission_amount"
:precision="2"
:step="0.01"
:controls="false"
style="width: 100%"
placeholder="请输入固定佣金金额(元)"
/>
<div class="form-tip">
该代理能获得的固定佣金金额单位
<span v-if="form.series_max_commission_amount > 0" class="max-amount-hint">
<br />
该系列最大佣金金额
<span class="amount-value"
>¥{{ form.series_max_commission_amount.toFixed(2) }}</span
>
</span>
</div>
</ElFormItem>
</ElCol>
</ElRow>
<!-- 梯度佣金配置 -->
<template v-if="form.commission_type === 'tiered'">
<ElFormItem label="梯度配置" prop="commission_tiers">
<ElTable :data="form.commission_tiers" border style="width: 100%">
<ElTableColumn label="比较运算符" width="100" align="center">
<template #default="{ row }">
<ElTag size="small" type="success">{{ row.operator || '>=' }}</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="达标阈值" width="120">
<template #default="{ row }">
<span class="readonly-value">{{ row.threshold }}</span>
</template>
</ElTableColumn>
<ElTableColumn label="统计维度" width="120">
<template #default="{ row }">
<ElTag size="small" type="info">
{{ row.dimension === 'sales_count' ? '销量' : '销售额' }}
</ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="统计范围" width="140">
<template #default="{ row }">
<ElTag size="small" type="warning"> 仅自己 </ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="佣金金额(元)" min-width="180">
<template #default="{ row }">
<div style="display: flex; flex-direction: column; gap: 4px">
<ElInputNumber
v-model="row.amount"
:min="0"
:max="row.max_amount"
:precision="2"
:step="0.01"
:controls="false"
placeholder="请输入佣金金额"
style="width: 100%"
/>
<span
v-if="row.max_amount"
style="font-size: 12px; color: var(--el-text-color-secondary)"
>
最大: ¥{{ row.max_amount.toFixed(2) }}
</span>
</div>
</template>
</ElTableColumn>
</ElTable>
<div class="form-tip" style="margin-top: 8px">
梯度配置从套餐系列继承达标阈值统计维度统计范围为只读只能修改佣金金额
</div>
</ElFormItem>
</template>
<!-- 强制充值配置 -->
<div class="form-section-title">
<span class="title-text">强制充值配置可选</span>
</div>
<!-- 启用强制充值和强充金额 - 2列布局 -->
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="启用强制充值">
<ElSwitch v-model="form.enable_force_recharge" />
</ElFormItem>
</ElCol>
<ElCol :span="12" v-if="form.enable_force_recharge">
<ElFormItem label="强充金额(元)" prop="force_recharge_amount">
<ElInputNumber
v-model="form.force_recharge_amount"
:min="0"
:precision="2"
:step="0.01"
:controls="false"
style="width: 100%"
placeholder="请输入强制充值金额(元)"
/>
<div class="form-tip">
用户需要达到的强制充值金额
<span
v-if="form.series_name && form.series_force_recharge_amount > 0"
class="series-force-hint"
>
<br />
可参考
<span class="amount-value">{{ form.series_name }}</span
>系列强充金额
<span class="amount-value"
>¥{{ form.series_force_recharge_amount.toFixed(2) }}</span
>
</span>
</div>
</ElFormItem>
</ElCol>
</ElRow>
</div>
</ElForm>
</template>
<!-- 编辑模式原有的表单布局 -->
<ElForm v-else ref="formRef" :model="form" :rules="rules" label-width="130px">
<!-- 编辑模式显示只读信息 -->
<div v-if="dialogType === 'edit'" class="info-row">
<div class="info-row">
<div class="info-item">
<span class="info-label">系列名称:</span>
<span class="info-value">{{ form.series_name || '-' }}</span>
@@ -189,15 +578,7 @@
</ElTableColumn>
<ElTableColumn label="统计范围" width="140">
<template #default="{ row }">
<ElTag size="small" type="warning">
{{
row.stat_scope === 'self'
? '仅自己'
: row.stat_scope === 'self_and_sub'
? '自己+下级'
: '-'
}}
</ElTag>
<ElTag size="small" type="warning"> 仅自己 </ElTag>
</template>
</ElTableColumn>
<ElTableColumn label="佣金金额(元)" min-width="180">
@@ -271,13 +652,13 @@
</ElCol>
</ElRow>
<!-- 套餐配置 -->
<div class="form-section-title">
<!-- 套餐配置(编辑模式下禁用) -->
<!-- <div class="form-section-title">
<span class="title-text">套餐配置可选</span>
</div>
</div> -->
<!-- 选择套餐 -->
<ElFormItem label="选择套餐">
<!-- 编辑模式下不显示套餐配置区域请使用右键菜单中的"添加授权套餐"功能 -->
<!-- <ElFormItem label="选择套餐">
<ElSelect
v-model="selectedPackageIds"
placeholder="请选择套餐"
@@ -300,10 +681,10 @@
/>
</ElSelect>
<div class="form-tip">选择该授权下包含的套餐</div>
</ElFormItem>
</ElFormItem> -->
<!-- 套餐成本价 -->
<ElFormItem label="套餐成本价" v-if="form.packages.length > 0">
<!-- <ElFormItem label="套餐成本价" v-if="form.packages.length > 0">
<div class="package-list">
<div
v-for="(pkg, index) in form.packages"
@@ -329,14 +710,32 @@
</div>
</div>
<div class="form-tip">设置每个套餐的成本价单位不能低于套餐原始成本价</div>
</ElFormItem>
</ElFormItem> -->
</ElForm>
<template #footer>
<div class="dialog-footer">
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
提交
</ElButton>
<template v-if="dialogType === 'add'">
<ElButton v-if="currentStep > 0" @click="handlePrevStep">上一步</ElButton>
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton v-if="currentStep < 1" type="primary" @click="handleNextStep"
>下一步</ElButton
>
<ElButton
v-else
type="primary"
@click="handleSubmit(formRef)"
:loading="submitLoading"
>
提交
</ElButton>
</template>
<template v-else>
<ElButton @click="dialogVisible = false">取消</ElButton>
<ElButton type="primary" @click="handleSubmit(formRef)" :loading="submitLoading">
提交
</ElButton>
</template>
</div>
</template>
</ElDialog>
@@ -346,7 +745,7 @@
</template>
<script setup lang="ts">
import { h } from 'vue'
import { h, ref, reactive, computed, watch, onMounted, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import {
ShopSeriesGrantService,
@@ -354,20 +753,34 @@
ShopService,
PackageManageService
} from '@/api/modules'
import { ElMessage, ElMessageBox, ElSwitch, ElTag, ElRow, ElCol } from 'element-plus'
import {
ElMessage,
ElMessageBox,
ElSwitch,
ElTag,
ElRow,
ElCol,
ElSteps,
ElStep,
ElEmpty
} from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import type {
ShopSeriesGrantResponse,
PackageSeriesResponse,
ShopResponse,
PackageResponse,
CommissionTier
CommissionTier,
GrantPackageItem,
GrantPackageInfo
} from '@/types/api'
import type { SearchFormItem } from '@/types'
import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth'
import { useTableContextMenu } from '@/composables/useTableContextMenu'
import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue'
import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue'
import TableContextMenuHint from '@/components/core/others/TableContextMenuHint.vue'
import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue'
import { formatDateTime } from '@/utils/business/format'
import {
@@ -392,6 +805,42 @@
const formRef = ref<FormInstance>()
const contextMenuRef = ref<InstanceType<typeof ArtMenuRight>>()
const currentRow = ref<ShopSeriesGrantResponse | null>(null)
const currentStep = ref(0) // 当前步骤0 为第一步1 为第二步
// 套餐列表对话框相关
const packageListDialogVisible = ref(false)
const currentGrantId = ref<number>(0)
const currentGrantSeriesId = ref<number>(0)
const currentGrantPackages = ref<GrantPackageInfo[]>([])
// 套餐管理相关
const packageDialogVisible = ref(false)
const packageDialogType = ref<'add' | 'edit'>('add')
const packageSubmitLoading = ref(false)
const availablePackages = ref<PackageResponse[]>([])
const packageFormRef = ref<FormInstance>()
// 套餐表单
const packageForm = ref<{
package_id?: number
package_name?: string
package_code?: string
cost_price_yuan: number
}>({
cost_price_yuan: 0
})
// 套餐表单验证规则
const packageFormRules = computed<FormRules>(() => ({
package_id: [
{ required: packageDialogType.value === 'add', message: '请选择套餐', trigger: 'change' }
],
cost_price_yuan: [
{ required: true, message: '请输入成本价', trigger: 'blur' },
{ type: 'number', min: 0, message: '成本价不能小于0', trigger: 'blur' }
]
}))
const seriesOptions = ref<PackageSeriesResponse[]>([])
const shopOptions = ref<ShopResponse[]>([])
const shopTreeData = ref<ShopResponse[]>([])
@@ -1277,6 +1726,8 @@
const handleDialogClosed = () => {
// 清除表单验证状态
formRef.value?.clearValidate()
// 重置步骤
currentStep.value = 0
// 重置表单数据
form.id = 0
form.series_id = undefined
@@ -1414,6 +1865,10 @@
const contextMenuItems = computed((): MenuItemType[] => {
const items: MenuItemType[] = []
if (hasAuth('series_grants:detail')) {
items.push({ key: 'view_packages', label: '查看套餐列表' })
}
if (hasAuth('series_grants:edit')) {
items.push({ key: 'edit', label: '编辑' })
}
@@ -1438,6 +1893,9 @@
if (!currentRow.value) return
switch (item.key) {
case 'view_packages':
showPackageListDialog(currentRow.value)
break
case 'edit':
showDialog('edit', currentRow.value)
break
@@ -1446,6 +1904,242 @@
break
}
}
// 显示套餐列表对话框
const showPackageListDialog = async (row: ShopSeriesGrantResponse) => {
currentGrantId.value = row.id
currentGrantSeriesId.value = row.series_id
// 获取该授权的详细信息,包括套餐列表
loading.value = true
try {
const res = await ShopSeriesGrantService.getShopSeriesGrantDetail(row.id)
if (res.code === 0) {
currentGrantPackages.value = res.data.packages || []
packageListDialogVisible.value = true
}
} catch (error) {
console.error('获取套餐列表失败:', error)
ElMessage.error('获取套餐列表失败')
} finally {
loading.value = false
}
}
// 显示添加套餐对话框
const showAddPackageDialog = () => {
packageDialogType.value = 'add'
packageForm.value = {
package_id: undefined,
cost_price_yuan: 0
}
// 加载可用套餐
if (currentGrantSeriesId.value) {
loadAvailablePackages()
}
packageDialogVisible.value = true
}
// 显示编辑套餐对话框
const showEditPackageDialog = (row: GrantPackageInfo) => {
packageDialogType.value = 'edit'
packageForm.value = {
package_id: row.package_id,
package_name: row.package_name,
package_code: row.package_code,
cost_price_yuan: row.cost_price / 100
}
packageDialogVisible.value = true
}
// 加载可用套餐
const loadAvailablePackages = async (packageName?: string) => {
if (!currentGrantSeriesId.value) return
packageLoading.value = true
try {
const params: any = {
page: 1,
page_size: 50,
series_id: currentGrantSeriesId.value
}
if (packageName) {
params.package_name = packageName
}
const res = await PackageManageService.getPackages(params)
if (res.code === 0) {
availablePackages.value = res.data.items
}
} catch (error) {
console.error('加载套餐选项失败:', error)
} finally {
packageLoading.value = false
}
}
// 搜索可用套餐
const searchAvailablePackages = (query: string) => {
if (query) {
loadAvailablePackages(query)
} else {
loadAvailablePackages()
}
}
// 检查套餐是否已添加
const isPackageAlreadyAdded = (packageId: number) => {
return currentGrantPackages.value.some((p) => p.package_id === packageId)
}
// 保存套餐
const handleSavePackage = async () => {
if (!packageFormRef.value || !currentGrantId.value) return
await packageFormRef.value.validate(async (valid) => {
if (!valid) return
packageSubmitLoading.value = true
try {
const packages: GrantPackageItem[] = []
if (packageDialogType.value === 'add') {
// 添加模式:添加新套餐
packages.push({
package_id: packageForm.value.package_id,
cost_price: Math.round(packageForm.value.cost_price_yuan * 100)
})
} else {
// 编辑模式:更新套餐成本价
packages.push({
package_id: packageForm.value.package_id,
cost_price: Math.round(packageForm.value.cost_price_yuan * 100)
})
}
await ShopSeriesGrantService.manageGrantPackages(currentGrantId.value, { packages })
ElMessage.success(packageDialogType.value === 'add' ? '添加成功' : '更新成功')
packageDialogVisible.value = false
// 刷新套餐列表
await refreshPackageList()
} catch (error) {
console.error(error)
ElMessage.error(packageDialogType.value === 'add' ? '添加失败' : '更新失败')
} finally {
packageSubmitLoading.value = false
}
})
}
// 删除套餐
const handleDeletePackage = (row: GrantPackageInfo) => {
ElMessageBox.confirm(`确定删除套餐 ${row.package_name} 的授权吗?`, '删除确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
})
.then(async () => {
if (!currentGrantId.value) return
packageSubmitLoading.value = true
try {
const packages: GrantPackageItem[] = [
{
package_id: row.package_id,
remove: true
}
]
await ShopSeriesGrantService.manageGrantPackages(currentGrantId.value, { packages })
ElMessage.success('删除成功')
// 刷新套餐列表
await refreshPackageList()
} catch (error) {
console.error(error)
ElMessage.error('删除失败')
} finally {
packageSubmitLoading.value = false
}
})
.catch(() => {
// 用户取消
})
}
// 刷新套餐列表
const refreshPackageList = async () => {
if (!currentGrantId.value) return
try {
const res = await ShopSeriesGrantService.getShopSeriesGrantDetail(currentGrantId.value)
if (res.code === 0) {
currentGrantPackages.value = res.data.packages || []
}
} catch (error) {
console.error('刷新套餐列表失败:', error)
}
}
// 关闭套餐对话框
const handlePackageDialogClosed = () => {
packageFormRef.value?.resetFields()
packageForm.value = {
cost_price_yuan: 0
}
availablePackages.value = []
}
// 关闭套餐列表对话框
const handlePackageListDialogClosed = () => {
currentGrantId.value = 0
currentGrantSeriesId.value = 0
currentGrantPackages.value = []
availablePackages.value = []
}
// 使用表格右键菜单功能
const {
showContextMenuHint,
hintPosition,
getRowClassName,
handleCellMouseEnter,
handleCellMouseLeave
} = useTableContextMenu()
// 下一步
const handleNextStep = async () => {
if (!formRef.value) return
// 根据当前步骤验证不同的字段
let fieldsToValidate: string[] = []
if (currentStep.value === 0) {
// 第一步:验证系列和店铺选择
fieldsToValidate = ['series_id', 'shop_id']
}
try {
// 验证当前步骤的字段
await formRef.value.validateField(fieldsToValidate)
// 验证通过,进入下一步
currentStep.value++
} catch (error) {
console.error('表单验证失败:', error)
}
}
// 上一步
const handlePrevStep = () => {
if (currentStep.value > 0) {
currentStep.value--
}
}
</script>
<style lang="scss" scoped>
@@ -1453,6 +2147,10 @@
// 可以添加特定样式
}
:deep(.el-table__row.table-row-with-context-menu) {
cursor: pointer;
}
.dialog-footer {
text-align: right;
}
@@ -1595,4 +2293,17 @@
color: var(--el-text-color-primary);
font-weight: 500;
}
.package-list-dialog-content {
.package-list-header {
display: flex;
justify-content: flex-end;
margin-bottom: 12px;
}
.amount-value {
font-weight: 600;
color: var(--el-color-warning);
}
}
</style>