From b94c043a56f6b8413329d13748b46a512a0cd0c4 Mon Sep 17 00:00:00 2001 From: sexygoat <1538832180@qq.com> Date: Thu, 5 Feb 2026 17:22:41 +0800 Subject: [PATCH] =?UTF-8?q?fetch(modify):=E4=BF=AE=E6=94=B9bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/assets/styles/app.scss | 13 + src/components/common/DetailPage.vue | 159 +++++ src/components/core/forms/ArtButtonTable.vue | 5 +- src/locales/langs/en.json | 8 +- src/locales/langs/zh.json | 8 +- src/router/routes/asyncRoutes.ts | 68 +- src/router/routesAlias.ts | 6 + src/types/api/packageManagement.ts | 14 +- src/types/components.d.ts | 1 + .../account-management/account/index.vue | 30 +- .../enterprise-customer/index.vue | 118 +++- .../platform-account/index.vue | 30 +- .../account-management/shop-account/index.vue | 8 +- src/views/common/account-list.vue | 300 ++++++++ .../package-assign/detail.vue | 149 ++++ .../package-assign/index.vue | 392 ++++++----- .../package-create/index.vue | 4 +- .../package-list/detail.vue | 248 +++++++ .../package-management/package-list/index.vue | 181 ++--- .../package-series/detail.vue | 263 +++++++ .../package-series/index.vue | 650 +++++++++++++++++- .../series-assign/detail.vue | 207 ++++++ .../series-assign/index.vue | 293 ++++++-- src/views/product/shop/index.vue | 25 +- 24 files changed, 2734 insertions(+), 446 deletions(-) create mode 100644 src/components/common/DetailPage.vue create mode 100644 src/views/common/account-list.vue create mode 100644 src/views/package-management/package-assign/detail.vue create mode 100644 src/views/package-management/package-list/detail.vue create mode 100644 src/views/package-management/package-series/detail.vue create mode 100644 src/views/package-management/series-assign/detail.vue diff --git a/src/assets/styles/app.scss b/src/assets/styles/app.scss index 34ca860..fd9665f 100644 --- a/src/assets/styles/app.scss +++ b/src/assets/styles/app.scss @@ -193,3 +193,16 @@ body { align-items: center; gap: 20px; } + +// 表单分段标题样式 - 用于对话框中的表单分段 +.form-section-title { + margin: 24px 0 16px 0; + padding-left: 12px; + border-left: 3px solid var(--el-color-primary); + + .title-text { + font-size: 14px; + font-weight: 600; + color: var(--el-text-color-primary); + } +} diff --git a/src/components/common/DetailPage.vue b/src/components/common/DetailPage.vue new file mode 100644 index 0000000..48007db --- /dev/null +++ b/src/components/common/DetailPage.vue @@ -0,0 +1,159 @@ + + + + + diff --git a/src/components/core/forms/ArtButtonTable.vue b/src/components/core/forms/ArtButtonTable.vue index 6602400..572f7ed 100644 --- a/src/components/core/forms/ArtButtonTable.vue +++ b/src/components/core/forms/ArtButtonTable.vue @@ -13,7 +13,7 @@ const props = withDefaults( defineProps<{ text?: string - type?: 'add' | 'edit' | 'delete' | 'more' + type?: 'add' | 'edit' | 'delete' | 'more' | 'view' icon?: string // 自定义图标 iconClass?: BgColorEnum // 自定义按钮背景色、文字颜色 iconColor?: string // 外部传入的图标文字颜色 @@ -31,7 +31,8 @@ { type: 'add', icon: '', color: BgColorEnum.PRIMARY }, { type: 'edit', icon: '', color: BgColorEnum.SECONDARY }, { type: 'delete', icon: '', color: BgColorEnum.ERROR }, - { type: 'more', icon: '', color: '' } + { type: 'more', icon: '', color: '' }, + { type: 'view', icon: '', color: BgColorEnum.SECONDARY } ] as const // 计算最终使用的图标:优先使用外部传入的 icon,否则根据 type 获取默认图标 diff --git a/src/locales/langs/en.json b/src/locales/langs/en.json index 26ba8d8..15fd372 100644 --- a/src/locales/langs/en.json +++ b/src/locales/langs/en.json @@ -390,10 +390,14 @@ "packageCreate": "Create Package", "packageBatch": "Batch Management", "packageList": "My Packages", + "packageDetail": "Package Detail", "packageChange": "Package Change", "packageAssign": "Package Assignment", + "packageAssignDetail": "Package Assignment Detail", "seriesAssign": "Series Assignment", + "seriesAssignDetail": "Series Assignment Detail", "packageSeries": "Package Series", + "packageSeriesDetail": "Package Series Detail", "packageCommission": "Package Commission Cards" }, "accountManagement": { @@ -406,6 +410,7 @@ "agent": "Agent Management", "customerAccount": "Customer Account", "enterpriseCustomer": "Enterprise Customer", + "enterpriseCustomerAccounts": "Enterprise Customer Accounts", "enterpriseCards": "Enterprise Card Management", "customerCommission": "Customer Commission" }, @@ -429,7 +434,8 @@ "packageSeries": "Package Series Management", "packageList": "Package Management", "packageAssign": "Package Assignment", - "shop": "Shop Management" + "shop": "Shop Management", + "shopAccounts": "Shop Accounts" }, "assetManagement": { "title": "Asset Management", diff --git a/src/locales/langs/zh.json b/src/locales/langs/zh.json index 7bbc4b3..9160776 100644 --- a/src/locales/langs/zh.json +++ b/src/locales/langs/zh.json @@ -402,10 +402,14 @@ "packageCreate": "新建套餐", "packageBatch": "批量管理", "packageList": "套餐管理", + "packageDetail": "套餐详情", "packageChange": "套餐变更", "packageAssign": "单套餐分配", + "packageAssignDetail": "套餐分配详情", "seriesAssign": "套餐系列分配", + "seriesAssignDetail": "系列分配详情", "packageSeries": "套餐系列", + "packageSeriesDetail": "套餐系列详情", "packageCommission": "套餐佣金网卡" }, "accountManagement": { @@ -418,6 +422,7 @@ "agent": "代理商管理", "customerAccount": "客户账号", "enterpriseCustomer": "企业客户", + "enterpriseCustomerAccounts": "企业客户账号列表", "enterpriseCards": "企业卡管理", "customerCommission": "客户账号佣金" }, @@ -432,7 +437,8 @@ "packageSeries": "套餐系列管理", "packageList": "套餐管理", "packageAssign": "套餐分配", - "shop": "店铺管理" + "shop": "店铺管理", + "shopAccounts": "店铺账号列表" }, "assetManagement": { "title": "资产管理", diff --git a/src/router/routes/asyncRoutes.ts b/src/router/routes/asyncRoutes.ts index 383a89f..f6d3a3b 100644 --- a/src/router/routes/asyncRoutes.ts +++ b/src/router/routes/asyncRoutes.ts @@ -721,6 +721,16 @@ export const asyncRoutes: AppRouteRecord[] = [ keepAlive: true } }, + { + path: 'package-series/detail/:id', + name: 'PackageSeriesDetail', + component: RoutesAlias.PackageSeriesDetail, + meta: { + title: 'menus.packageManagement.packageSeriesDetail', + isHide: true, + keepAlive: false + } + }, { path: 'package-list', name: 'PackageList', @@ -730,6 +740,16 @@ export const asyncRoutes: AppRouteRecord[] = [ keepAlive: true } }, + { + path: 'package-list/detail/:id', + name: 'PackageDetail', + component: RoutesAlias.PackageDetail, + meta: { + title: 'menus.packageManagement.packageDetail', + isHide: true, + keepAlive: false + } + }, { path: 'package-assign', name: 'PackageAssign', @@ -739,6 +759,16 @@ export const asyncRoutes: AppRouteRecord[] = [ keepAlive: true } }, + { + path: 'package-assign/detail/:id', + name: 'PackageAssignDetail', + component: RoutesAlias.PackageAssignDetail, + meta: { + title: 'menus.packageManagement.packageAssignDetail', + isHide: true, + keepAlive: false + } + }, { path: 'series-assign', name: 'SeriesAssign', @@ -747,6 +777,16 @@ export const asyncRoutes: AppRouteRecord[] = [ title: 'menus.packageManagement.seriesAssign', keepAlive: true } + }, + { + path: 'series-assign/detail/:id', + name: 'SeriesAssignDetail', + component: RoutesAlias.SeriesAssignDetail, + meta: { + title: 'menus.packageManagement.seriesAssignDetail', + isHide: true, + keepAlive: false + } } ] }, @@ -824,6 +864,25 @@ export const asyncRoutes: AppRouteRecord[] = [ // keepAlive: true // } // }, + { + path: 'enterprise-customer', + name: 'EnterpriseCustomer', + component: RoutesAlias.EnterpriseCustomer, + meta: { + title: 'menus.accountManagement.enterpriseCustomer', + keepAlive: true + } + }, + { + path: 'enterprise-customer/customer-accounts/:id', + name: 'EnterpriseCustomerAccounts', + component: RoutesAlias.EnterpriseCustomerAccounts, + meta: { + title: 'menus.accountManagement.enterpriseCustomerAccounts', + isHide: true, + keepAlive: false + } + }, { path: 'enterprise-cards', name: 'EnterpriseCards', @@ -1013,15 +1072,6 @@ export const asyncRoutes: AppRouteRecord[] = [ // keepAlive: true // } // }, - { - path: 'enterprise-customer', - name: 'EnterpriseCustomer', - component: RoutesAlias.EnterpriseCustomer, - meta: { - title: 'menus.accountManagement.enterpriseCustomer', - keepAlive: true - } - }, { path: 'carrier-management', name: 'CarrierManagement', diff --git a/src/router/routesAlias.ts b/src/router/routesAlias.ts index afb3917..37705ca 100644 --- a/src/router/routesAlias.ts +++ b/src/router/routesAlias.ts @@ -68,10 +68,14 @@ export enum RoutesAlias { PackageCreate = '/package-management/package-create', // 新建套餐 PackageBatch = '/package-management/package-batch', // 批量管理 PackageList = '/package-management/package-list', // 套餐管理 + PackageDetail = '/package-management/package-list/detail', // 套餐详情 PackageChange = '/package-management/package-change', // 套餐变更 PackageAssign = '/package-management/package-assign', // 单套餐分配 + PackageAssignDetail = '/package-management/package-assign/detail', // 单套餐分配详情 SeriesAssign = '/package-management/series-assign', // 套餐系列分配 + SeriesAssignDetail = '/package-management/series-assign/detail', // 套餐系列分配详情 PackageSeries = '/package-management/package-series', // 套餐系列 + PackageSeriesDetail = '/package-management/package-series/detail', // 套餐系列详情 PackageCommission = '/package-management/package-commission', // 套餐佣金网卡 // 账号管理 @@ -82,12 +86,14 @@ export enum RoutesAlias { AgentManagement = '/account-management/agent', // 代理商管理 ShopAccount = '/account-management/shop-account', // 代理账号管理 EnterpriseCustomer = '/account-management/enterprise-customer', // 企业客户管理 + EnterpriseCustomerAccounts = '/common/account-list', // 企业客户账号列表(通用) EnterpriseCards = '/account-management/enterprise-cards', // 企业卡管理 CustomerCommission = '/account-management/customer-commission', // 客户账号佣金 // 产品管理 SimCardManagement = '/product/sim-card', // 网卡产品管理 SimCardAssign = '/product/sim-card-assign', // 号卡分配 + ShopAccounts = '/common/account-list', // 店铺账号列表(通用) // 资产管理 StandaloneCardList = '/asset-management/iot-card-management', // IoT卡管理 diff --git a/src/types/api/packageManagement.ts b/src/types/api/packageManagement.ts index 4b7d8cb..e05e8aa 100644 --- a/src/types/api/packageManagement.ts +++ b/src/types/api/packageManagement.ts @@ -21,17 +21,17 @@ export interface OneTimeCommissionTier { * 套餐系列一次性佣金配置 */ export interface SeriesOneTimeCommissionConfig { - enable: boolean // 是否启用一次性佣金 - commission_type: 'fixed' | 'tiered' // 佣金类型:固定或梯度 + enable?: boolean // 是否启用一次性佣金 + commission_type?: 'fixed' | 'tiered' // 佣金类型:固定或梯度 commission_amount?: number // 固定佣金金额(分),commission_type=fixed时使用 - threshold: number // 触发阈值(分) - trigger_type: 'first_recharge' | 'accumulated_recharge' // 触发类型:首次充值或累计充值 + threshold?: number // 触发阈值(分) + trigger_type?: 'first_recharge' | 'accumulated_recharge' // 触发类型:首充或累计充值 tiers?: OneTimeCommissionTier[] | null // 梯度配置列表,commission_type=tiered时使用 - enable_force_recharge: boolean // 是否启用强充 + enable_force_recharge?: boolean // 是否启用强充 force_amount?: number // 强充金额(分) force_calc_type?: 'fixed' | 'dynamic' // 强充计算类型:固定或动态 - validity_type: 'permanent' | 'fixed_date' | 'relative' // 时效类型:永久、固定日期或相对时长 - validity_value?: string // 时效值(日期字符串或月数) + validity_type?: 'permanent' | 'fixed_date' | 'relative' // 时效类型:永久、固定日期或相对时长 + validity_value?: string // 时效值(日期或月数) } /** diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 5e84da7..f5fb055 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -79,6 +79,7 @@ declare module 'vue' { CommissionDisplay: typeof import('./../components/business/CommissionDisplay.vue')['default'] ContainerSettings: typeof import('./../components/core/layouts/art-settings-panel/widget/ContainerSettings.vue')['default'] CustomerAccountDialog: typeof import('./../components/business/CustomerAccountDialog.vue')['default'] + DetailPage: typeof import('./../components/common/DetailPage.vue')['default'] ElAlert: typeof import('element-plus/es')['ElAlert'] ElAvatar: typeof import('element-plus/es')['ElAvatar'] ElButton: typeof import('element-plus/es')['ElButton'] diff --git a/src/views/account-management/account/index.vue b/src/views/account-management/account/index.vue index d746079..1d47fe3 100644 --- a/src/views/account-management/account/index.vue +++ b/src/views/account-management/account/index.vue @@ -24,7 +24,7 @@ - + @@ -84,8 +84,8 @@ -
- +
+ {{ role.role_name }} import { h } from 'vue' + import { useRoute } from 'vue-router' import { FormInstance, ElSwitch, ElCheckbox, ElCheckboxGroup, ElTag } from 'element-plus' import { ElMessageBox, ElMessage } from 'element-plus' import type { FormRules } from 'element-plus' @@ -130,6 +131,7 @@ defineOptions({ name: 'Account' }) // 定义组件名称,用于 KeepAlive 缓存控制 const { hasAuth } = useAuth() + const route = useRoute() const dialogType = ref('add') const dialogVisible = ref(false) @@ -272,7 +274,7 @@ } if (type === 'edit' && row) { - formData.id = row.ID + formData.id = row.id formData.username = row.username formData.phone = row.phone formData.user_type = row.user_type @@ -295,7 +297,7 @@ }) .then(async () => { try { - await AccountService.deleteAccount(row.ID) + await AccountService.deleteAccount(row.id) ElMessage.success('删除成功') getAccountList() } catch (error) { @@ -425,6 +427,12 @@ }) onMounted(() => { + // 从 URL 查询参数中读取 shop_id + const shopIdParam = route.query.shop_id + if (shopIdParam) { + formFilters.shop_id = Number(shopIdParam) + } + getAccountList() loadAllRoles() loadShopList() @@ -444,7 +452,7 @@ // 显示分配角色对话框 const showRoleDialog = async (row: any) => { - currentAccountId.value = row.ID + currentAccountId.value = row.id selectedRoles.value = [] try { @@ -452,11 +460,11 @@ await loadAllRoles() // 先加载当前账号的角色,再打开对话框 - const res = await AccountService.getAccountRoles(row.ID) + const res = await AccountService.getAccountRoles(row.id) if (res.code === 0) { // 提取角色ID数组 const roles = res.data || [] - selectedRoles.value = roles.map((role: any) => role.ID) + selectedRoles.value = roles.map((role: any) => role.id) // 数据加载完成后再打开对话框 roleDialogVisible.value = true } @@ -582,7 +590,7 @@ // 先更新UI row.status = newStatus try { - await AccountService.updateAccountStatus(row.ID, newStatus as 0 | 1) + await AccountService.updateAccountStatus(row.id, newStatus as 0 | 1) ElMessage.success('状态切换成功') } catch (error) { // 切换失败,恢复原状态 diff --git a/src/views/account-management/enterprise-customer/index.vue b/src/views/account-management/enterprise-customer/index.vue index 5abeb8e..beeaef1 100644 --- a/src/views/account-management/enterprise-customer/index.vue +++ b/src/views/account-management/enterprise-customer/index.vue @@ -168,12 +168,6 @@ - - - @@ -199,6 +193,14 @@
+ + +
@@ -215,9 +217,11 @@ import { useCheckedColumns } from '@/composables/useCheckedColumns' import { useAuth } from '@/composables/useAuth' import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue' - import CustomerAccountDialog from '@/components/business/CustomerAccountDialog.vue' + import ArtMenuRight from '@/components/core/others/ArtMenuRight.vue' + import type { MenuItemType } from '@/components/core/others/ArtMenuRight.vue' import { formatDateTime } from '@/utils/business/format' import { BgColorEnum } from '@/enums/appEnum' + import { RoutesAlias } from '@/router/routesAlias' defineOptions({ name: 'EnterpriseCustomer' }) @@ -227,7 +231,6 @@ const dialogVisible = ref(false) const passwordDialogVisible = ref(false) - const customerAccountDialogVisible = ref(false) const loading = ref(false) const submitLoading = ref(false) const passwordSubmitLoading = ref(false) @@ -236,6 +239,10 @@ const currentEnterpriseId = ref(0) const shopList = ref([]) + // 右键菜单 + const enterpriseOperationMenuRef = ref>() + const currentOperatingEnterprise = ref(null) + // 搜索表单初始值 const initialSearchState = { enterprise_name: '', @@ -450,47 +457,30 @@ { prop: 'operation', label: '操作', - width: 340, + width: 200, fixed: 'right', formatter: (row: EnterpriseItem) => { const buttons = [] - if (hasAuth('enterprise_customer:edit')) { - buttons.push( - h(ArtButtonTable, { - text: '编辑', - iconClass: BgColorEnum.SECONDARY, - onClick: () => showDialog('edit', row) - }) - ) - } - - if (hasAuth('enterprise_customer:look_customer')) { - buttons.push( - h(ArtButtonTable, { - text: '查看客户', - iconClass: BgColorEnum.PRIMARY, - onClick: () => viewCustomerAccounts(row) - }) - ) - } - if (hasAuth('enterprise_customer:card_authorization')) { buttons.push( h(ArtButtonTable, { text: '卡授权', - iconClass: BgColorEnum.PRIMARY, onClick: () => manageCards(row) }) ) } - if (hasAuth('enterprise_customer:update_pwd')) { + // 只要有编辑、账号列表、修改密码权限之一,就显示更多操作按钮 + if ( + hasAuth('enterprise_customer:edit') || + hasAuth('enterprise_customer:look_customer') || + hasAuth('enterprise_customer:update_pwd') + ) { buttons.push( h(ArtButtonTable, { - text: '修改密码', - iconClass: BgColorEnum.WARNING, - onClick: () => showPasswordDialog(row) + text: '更多操作', + onContextmenu: (e: MouseEvent) => showEnterpriseOperationMenu(e, row) }) ) } @@ -749,8 +739,11 @@ // 查看客户账号 const viewCustomerAccounts = (row: EnterpriseItem) => { - currentEnterpriseId.value = row.id - customerAccountDialogVisible.value = true + router.push({ + name: 'EnterpriseCustomerAccounts', + params: { id: row.id }, + query: { type: 'enterprise' } + }) } // 卡管理 @@ -760,6 +753,59 @@ query: { id: row.id } }) } + + // 企业客户操作菜单项配置 + const enterpriseOperationMenuItems = computed((): MenuItemType[] => { + const items: MenuItemType[] = [] + + if (hasAuth('enterprise_customer:look_customer')) { + items.push({ + key: 'accountList', + label: '账号列表' + }) + } + + if (hasAuth('enterprise_customer:edit')) { + items.push({ + key: 'edit', + label: '编辑' + }) + } + + if (hasAuth('enterprise_customer:update_pwd')) { + items.push({ + key: 'updatePassword', + label: '修改密码' + }) + } + + return items + }) + + // 显示企业客户操作右键菜单 + const showEnterpriseOperationMenu = (e: MouseEvent, row: EnterpriseItem) => { + e.preventDefault() + e.stopPropagation() + currentOperatingEnterprise.value = row + enterpriseOperationMenuRef.value?.show(e) + } + + // 处理企业客户操作菜单选择 + const handleEnterpriseOperationMenuSelect = (item: MenuItemType) => { + if (!currentOperatingEnterprise.value) return + + switch (item.key) { + case 'accountList': + viewCustomerAccounts(currentOperatingEnterprise.value) + break + case 'edit': + showDialog('edit', currentOperatingEnterprise.value) + break + case 'updatePassword': + showPasswordDialog(currentOperatingEnterprise.value) + break + } + } diff --git a/src/views/package-management/package-assign/detail.vue b/src/views/package-management/package-assign/detail.vue new file mode 100644 index 0000000..8a4fb7b --- /dev/null +++ b/src/views/package-management/package-assign/detail.vue @@ -0,0 +1,149 @@ + + + + + diff --git a/src/views/package-management/package-assign/index.vue b/src/views/package-management/package-assign/index.vue index ebf9ee3..6ad5e9c 100644 --- a/src/views/package-management/package-assign/index.vue +++ b/src/views/package-management/package-assign/index.vue @@ -5,7 +5,8 @@ @@ -40,70 +41,15 @@
- - - - - - - - - - - - - - - - - - - - + - - - + :loading="shopLoading" + check-strictly + :render-after-expand="false" + /> import { h } from 'vue' - import { ShopPackageAllocationService, PackageManageService, ShopService } from '@/api/modules' + import { useRouter } from 'vue-router' + import { ShopPackageAllocationService, PackageManageService, ShopService, ShopSeriesAllocationService } from '@/api/modules' import { ElMessage, ElMessageBox, ElSwitch } from 'element-plus' import type { FormInstance, FormRules } from 'element-plus' import type { ShopPackageAllocationResponse, PackageResponse, ShopResponse } from '@/types/api' @@ -180,6 +122,7 @@ import { useAuth } from '@/composables/useAuth' import ArtButtonTable from '@/components/core/forms/ArtButtonTable.vue' import { formatDateTime } from '@/utils/business/format' + import { RoutesAlias } from '@/router/routesAlias' import { CommonStatus, getStatusText, @@ -190,26 +133,29 @@ defineOptions({ name: 'PackageAssign' }) const { hasAuth } = useAuth() + const router = useRouter() const dialogVisible = ref(false) - const costPriceDialogVisible = ref(false) const loading = ref(false) const submitLoading = ref(false) - const costPriceSubmitLoading = ref(false) const packageLoading = ref(false) const shopLoading = ref(false) const tableRef = ref() const formRef = ref() - const costPriceFormRef = ref() const packageOptions = ref([]) const shopOptions = ref([]) + const shopTreeData = ref([]) const searchPackageOptions = ref([]) const searchShopOptions = ref([]) + const searchAllocatorShopOptions = ref([]) + const searchSeriesAllocationOptions = ref([]) // 搜索表单初始值 const initialSearchState = { shop_id: undefined as number | undefined, package_id: undefined as number | undefined, + series_allocation_id: undefined as number | undefined, + allocator_shop_id: undefined as number | undefined, status: undefined as number | undefined } @@ -219,7 +165,7 @@ // 搜索表单配置 const searchFormItems = computed(() => [ { - label: '店铺', + label: '被分配店铺', prop: 'shop_id', type: 'select', config: { @@ -254,6 +200,44 @@ value: p.id })) }, + { + label: '系列分配', + prop: 'series_allocation_id', + type: 'select', + config: { + clearable: true, + filterable: true, + remote: true, + remoteMethod: handleSearchSeriesAllocation, + loading: loading.value, + placeholder: '请选择或搜索系列分配' + }, + options: () => + searchSeriesAllocationOptions.value.map((s) => ({ + label: `${s.series_name} - ${s.shop_name}`, + value: s.id + })) + }, + { + label: '分配者店铺', + prop: 'allocator_shop_id', + type: 'select', + config: { + clearable: true, + filterable: true, + remote: true, + remoteMethod: handleSearchAllocatorShop, + loading: shopLoading.value, + placeholder: '请选择或搜索分配者店铺' + }, + options: () => [ + { label: '平台', value: 0 }, + ...searchAllocatorShopOptions.value.map((s) => ({ + label: s.shop_name, + value: s.id + })) + ] + }, { label: '状态', prop: 'status', @@ -278,10 +262,11 @@ // 列配置 const columnOptions = [ - { label: 'ID', prop: 'id' }, { label: '套餐编码', prop: 'package_code' }, { label: '套餐名称', prop: 'package_name' }, - { label: '店铺名称', prop: 'shop_name' }, + { label: '系列名称', prop: 'series_name' }, + { label: '被分配店铺', prop: 'shop_name' }, + { label: '分配者', prop: 'allocator_shop_name' }, { label: '成本价', prop: 'cost_price' }, { label: '状态', prop: 'status' }, { label: '创建时间', prop: 'created_at' }, @@ -320,45 +305,47 @@ package_base_price: 0 // 存储选中套餐的成本价,用于验证 }) - // 成本价表单验证规则 - const costPriceRules = reactive({ - cost_price: [{ required: true, message: '请输入新成本价', trigger: 'blur' }] - }) - - // 成本价表单数据 - const costPriceForm = reactive({ - id: 0, - package_name: '', - shop_name: '', - old_cost_price: 0, - cost_price: 0 - }) - const allocationList = ref([]) const dialogType = ref('add') // 动态列配置 const { columnChecks, columns } = useCheckedColumns(() => [ - { - prop: 'id', - label: 'ID', - width: 80 - }, { prop: 'package_code', label: '套餐编码', - minWidth: 150 + minWidth: 200, + showOverflowTooltip: true }, { prop: 'package_name', label: '套餐名称', minWidth: 180 }, + { + prop: 'series_name', + label: '系列名称', + minWidth: 150 + }, { prop: 'shop_name', - label: '店铺名称', + label: '被分配店铺', minWidth: 180 }, + { + prop: 'allocator_shop_name', + label: '分配者', + formatter: (row: ShopPackageAllocationResponse) => { + // 如果是平台分配(allocator_shop_id为0),显示"平台"标签 + if (row.allocator_shop_id === 0) { + return h( + 'span', + { style: 'color: #409eff; font-weight: bold' }, + row.allocator_shop_name || '平台' + ) + } + return row.allocator_shop_name + } + }, { prop: 'cost_price', label: '成本价', @@ -399,24 +386,22 @@ { prop: 'operation', label: '操作', - width: 230, + width: 200, fixed: 'right', formatter: (row: ShopPackageAllocationResponse) => { const buttons = [] - if (hasAuth('package_assign:update_cost')) { - buttons.push( - h(ArtButtonTable, { - text: '修改成本价', - onClick: () => showCostPriceDialog(row) - }) - ) - } + buttons.push( + h(ArtButtonTable, { + type:"view", + onClick: () => handleViewDetail(row) + }) + ) if (hasAuth('package_assign:edit')) { buttons.push( h(ArtButtonTable, { - type: 'edit', + type:"edit", onClick: () => showDialog('edit', row) }) ) @@ -425,7 +410,7 @@ if (hasAuth('package_assign:delete')) { buttons.push( h(ArtButtonTable, { - type: 'delete', + type:"delete", onClick: () => deleteAllocation(row) }) ) @@ -436,11 +421,40 @@ } ]) + // 构建树形结构数据 + const buildTreeData = (items: ShopResponse[]) => { + const map = new Map() + const tree: ShopResponse[] = [] + + // 先将所有项放入 map + items.forEach((item) => { + map.set(item.id, { ...item, children: [] }) + }) + + // 构建树形结构 + items.forEach((item) => { + const node = map.get(item.id)! + if (item.parent_id && map.has(item.parent_id)) { + // 有父节点,添加到父节点的 children 中 + const parent = map.get(item.parent_id)! + if (!parent.children) parent.children = [] + parent.children.push(node) + } else { + // 没有父节点或父节点不存在,作为根节点 + tree.push(node) + } + }) + + return tree + } + onMounted(() => { loadPackageOptions() loadShopOptions() loadSearchPackageOptions() loadSearchShopOptions() + loadSearchAllocatorShopOptions() + loadSearchSeriesAllocationOptions() getTableData() }) @@ -466,20 +480,19 @@ } } - // 加载店铺选项(用于新增对话框,默认加载10条) - const loadShopOptions = async (shopName?: string) => { + // 加载店铺选项(用于新增对话框,加载所有店铺并构建树形结构) + const loadShopOptions = async () => { shopLoading.value = true try { - const params: any = { + // 加载所有店铺,不分页 + const res = await ShopService.getShops({ page: 1, - page_size: 10 - } - if (shopName) { - params.shop_name = shopName - } - const res = await ShopService.getShops(params) + page_size: 10000 // 使用较大的值获取所有店铺 + }) if (res.code === 0) { shopOptions.value = res.data.items || [] + // 构建树形结构数据 + shopTreeData.value = buildTreeData(shopOptions.value) } } catch (error) { console.error('加载店铺选项失败:', error) @@ -524,15 +537,6 @@ } } - // 搜索店铺(用于新增对话框) - const searchShop = (query: string) => { - if (query) { - loadShopOptions(query) - } else { - loadShopOptions() - } - } - // 搜索套餐(用于搜索栏) const handleSearchPackage = async (query: string) => { if (!query) { @@ -573,6 +577,73 @@ } } + // 加载搜索栏分配者店铺选项(默认加载10条) + const loadSearchAllocatorShopOptions = async () => { + try { + const res = await ShopService.getShops({ page: 1, page_size: 10 }) + if (res.code === 0) { + searchAllocatorShopOptions.value = res.data.items || [] + } + } catch (error) { + console.error('加载搜索栏分配者店铺选项失败:', error) + } + } + + // 搜索分配者店铺(用于搜索栏) + const handleSearchAllocatorShop = async (query: string) => { + if (!query) { + loadSearchAllocatorShopOptions() + return + } + try { + const res = await ShopService.getShops({ + page: 1, + page_size: 10, + shop_name: query + }) + if (res.code === 0) { + searchAllocatorShopOptions.value = res.data.items || [] + } + } catch (error) { + console.error('搜索分配者店铺失败:', error) + } + } + + // 加载搜索栏系列分配选项(默认加载10条) + const loadSearchSeriesAllocationOptions = async () => { + try { + const res = await ShopSeriesAllocationService.getShopSeriesAllocations({ + page: 1, + page_size: 10 + }) + if (res.code === 0) { + searchSeriesAllocationOptions.value = res.data.items || [] + } + } catch (error) { + console.error('加载搜索栏系列分配选项失败:', error) + } + } + + // 搜索系列分配(用于搜索栏) + const handleSearchSeriesAllocation = async (query: string) => { + if (!query) { + loadSearchSeriesAllocationOptions() + return + } + try { + const res = await ShopSeriesAllocationService.getShopSeriesAllocations({ + page: 1, + page_size: 10, + series_name: query + }) + if (res.code === 0) { + searchSeriesAllocationOptions.value = res.data.items || [] + } + } catch (error) { + console.error('搜索系列分配失败:', error) + } + } + // 获取分配列表 const getTableData = async () => { loading.value = true @@ -582,6 +653,8 @@ page_size: pagination.page_size, shop_id: searchForm.shop_id || undefined, package_id: searchForm.package_id || undefined, + series_allocation_id: searchForm.series_allocation_id || undefined, + allocator_shop_id: searchForm.allocator_shop_id || undefined, status: searchForm.status || undefined } const res = await ShopPackageAllocationService.getShopPackageAllocations(params) @@ -657,9 +730,9 @@ // 从套餐选项中找到选中的套餐 const selectedPackage = packageOptions.value.find((pkg) => pkg.id === packageId) if (selectedPackage) { - // 将套餐的价格(分)转换为元显示 - form.cost_price = selectedPackage.price / 100 - form.package_base_price = selectedPackage.price // 保持原始值(分)用于验证 + // 将套餐的成本价(分)转换为元显示 + form.cost_price = selectedPackage.cost_price / 100 + form.package_base_price = selectedPackage.cost_price // 保持原始值(分)用于验证 } } else { // 清空时重置成本价 @@ -744,60 +817,6 @@ }) } - // 显示修改成本价对话框 - const showCostPriceDialog = (row: ShopPackageAllocationResponse) => { - costPriceDialogVisible.value = true - costPriceForm.id = row.id - costPriceForm.package_name = row.package_name - costPriceForm.shop_name = row.shop_name - costPriceForm.old_cost_price = row.cost_price / 100 // 分转换为元显示 - costPriceForm.cost_price = row.cost_price / 100 // 分转换为元显示 - - // 重置表单验证状态 - nextTick(() => { - costPriceFormRef.value?.clearValidate() - }) - } - - // 处理成本价弹窗关闭事件 - const handleCostPriceDialogClosed = () => { - // 清除表单验证状态 - costPriceFormRef.value?.clearValidate() - // 重置表单数据 - costPriceForm.id = 0 - costPriceForm.package_name = '' - costPriceForm.shop_name = '' - costPriceForm.old_cost_price = 0 - costPriceForm.cost_price = 0 - } - - // 提交成本价修改 - const handleCostPriceSubmit = async (formEl: FormInstance | undefined) => { - if (!formEl) return - - await formEl.validate(async (valid) => { - if (valid) { - costPriceSubmitLoading.value = true - try { - // 将元转换为分提交给后端 - const costPriceInCents = Math.round(costPriceForm.cost_price * 100) - - await ShopPackageAllocationService.updateShopPackageAllocationCostPrice( - costPriceForm.id, - costPriceInCents - ) - ElMessage.success('修改成本价成功') - costPriceDialogVisible.value = false - await getTableData() - } catch (error) { - console.error(error) - } finally { - costPriceSubmitLoading.value = false - } - } - }) - } - // 状态切换 const handleStatusChange = async ( row: ShopPackageAllocationResponse, @@ -814,6 +833,11 @@ console.error(error) } } + + // 查看详情 + const handleViewDetail = (row: ShopPackageAllocationResponse) => { + router.push(`${RoutesAlias.PackageAssignDetail}/${row.id}`) + } diff --git a/src/views/package-management/package-list/index.vue b/src/views/package-management/package-list/index.vue index c712430..0470d81 100644 --- a/src/views/package-management/package-list/index.vue +++ b/src/views/package-management/package-list/index.vue @@ -5,7 +5,7 @@ @@ -48,7 +48,7 @@ :close-on-click-modal="false" @closed="handleDialogClosed" > - +
- - - - - - + + + + - + + + + @@ -178,6 +186,7 @@ diff --git a/src/views/package-management/package-series/index.vue b/src/views/package-management/package-series/index.vue index 72421a3..bbe8ff2 100644 --- a/src/views/package-management/package-series/index.vue +++ b/src/views/package-management/package-series/index.vue @@ -6,6 +6,7 @@ v-model:filter="searchForm" :items="searchFormItems" :show-expand="false" + label-width="85" @reset="handleReset" @search="handleSearch" > @@ -18,7 +19,9 @@ @refresh="handleRefresh" > @@ -44,18 +47,18 @@ -
+
生成编码 @@ -75,6 +78,261 @@ show-word-limit /> + + +
+ 一次性佣金配置 +
+ + + + + +