feat:代理现金余额不足100元展示、店铺业务员选择展示与筛选
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 5m34s

This commit is contained in:
luo
2026-07-20 12:17:02 +08:00
parent ca338dd345
commit e95dc8e4f5
12 changed files with 334 additions and 49 deletions

View File

@@ -8,9 +8,8 @@
- 在代理资金概况中展示现金余额、冻结金额和基于后端 `low_balance_warning` 的“现金余额不足100元”红色状态。 - 在代理资金概况中展示现金余额、冻结金额和基于后端 `low_balance_warning` 的“现金余额不足100元”红色状态。
- 复用 `GET /api/admin/shops/fund-summary`,读取 `balance``frozen_balance``cash_available``low_balance_warning` 字段,金额统一按分转元展示。 - 复用 `GET /api/admin/shops/fund-summary`,读取 `balance``frozen_balance``cash_available``low_balance_warning` 字段,金额统一按分转元展示。
- 复用通知列表与通知目标解析接口,实现低余额通知跳转到对应店铺资金页。
- 明确前端不得自行计算 100 元阈值,也不得展示阈值输入框。 - 明确前端不得自行计算 100 元阈值,也不得展示阈值输入框。
- 不包含公共站内通知中心页面的实现改造。 - 不包含任何公共站内通知相关功能的实现改造。
## Impact ## Impact
@@ -18,7 +17,6 @@
- Affected code: - Affected code:
- `src/types/api/commission.ts` - `src/types/api/commission.ts`
- `src/views/commission-management/agent-fund-overview/index.vue` - `src/views/commission-management/agent-fund-overview/index.vue`
- 顶部通知抽屉相关通知跳转逻辑
- Out of scope: - Out of scope:
- 公共站内通知中心页面的开发或改造 - 顶部通知抽屉、站内通知中心及其他公共站内通知功能的开发或改造
- 任意余额预警阈值配置能力 - 任意余额预警阈值配置能力

View File

@@ -36,25 +36,12 @@ The frontend SHALL read the low-balance display state exclusively from the `GET
- **THEN** 前端 MUST 读取 `balance``frozen_balance``cash_available``low_balance_warning` - **THEN** 前端 MUST 读取 `balance``frozen_balance``cash_available``low_balance_warning`
- **AND** `low_balance_warning` MUST be the only source of truth for whether the warning is shown - **AND** `low_balance_warning` MUST be the only source of truth for whether the warning is shown
### Requirement: Low Balance Notification Target Navigation ### Requirement: Public Notification Features Are Out Of Scope
The system SHALL allow users to navigate from low-balance notifications in the top notification drawer to the corresponding shop's fund overview page by reusing the notification list and notification target resolution APIs. Unauthorized targets SHALL follow the shared error-page behavior. This change SHALL NOT add or modify top notification drawer, notification center, or any other public in-app notification feature.
#### Scenario: Navigate from low balance notification to shop fund overview #### Scenario: Deliver low balance overview without notification changes
- **GIVEN** 用户在顶部通知抽屉中看到低余额通知
- **WHEN** 用户点击该通知
- **THEN** 前端 MUST 复用 `GET /api/admin/notifications/{id}/target` 解析跳转目标
- **AND** 系统 MUST 导航到对应店铺的资金页
#### Scenario: Handle unauthorized notification target
- **GIVEN** 用户点击低余额通知
- **WHEN** 通知目标页无权限访问或目标不可访问
- **THEN** 系统 MUST 按统一错误页处理
#### Scenario: Public notification center is out of scope
- **WHEN** 本次变更落地 - **WHEN** 本次变更落地
- **THEN** 公共站内通知中心页面 MUST NOT be required for this change to be considered complete - **THEN** 代理资金概况的低余额展示 MUST be available
- **AND** 顶部通知抽屉跳转能力仍 MUST be supported - **AND** 顶部通知抽屉、站内通知中心及其他公共站内通知功能 MUST NOT be changed by this proposal

View File

@@ -1,22 +1,15 @@
## 1. API Contract ## 1. API Contract
- [ ] 1.1 更新代理资金概况相关数据模型,补充 `balance``frozen_balance``cash_available``low_balance_warning` 字段读取。 - [x] 1.1 更新代理资金概况相关数据模型,补充 `balance``frozen_balance``cash_available``low_balance_warning` 字段读取。
- [ ] 1.2 复用 `GET /api/admin/notifications``GET /api/admin/notifications/{id}/target` 的既有通知接口,不新增站内通知中心专用接口。
## 2. Agent Fund Overview UI ## 2. Agent Fund Overview UI
- [ ] 2.1 在代理资金概况中展示现金余额、冻结金额,并统一按分转元格式化。 - [x] 2.1 在代理资金概况中展示现金余额、冻结金额,并统一按分转元格式化。
- [ ] 2.2 当 `low_balance_warning=true` 时展示“现金余额不足100元”红色状态。 - [x] 2.2 当 `low_balance_warning=true` 时展示“现金余额不足100元”红色状态。
- [ ] 2.3 不展示阈值输入框,不把信用额度计入预警文案,也不由前端自行计算余额阈值。 - [x] 2.3 不展示阈值输入框,不把信用额度计入预警文案,也不由前端自行计算余额阈值。
## 3. Notification Navigation ## 3. Verification
- [ ] 3.1 在顶部通知抽屉中支持低余额通知跳转到对应店铺资金页 - [ ] 3.1 验证现金可用余额高于、等于、低于100元三种情况下的状态展示正确且仅由 `low_balance_warning` 控制
- [ ] 3.2 当用户无目标页面权限或目标不可访问时,按统一错误页处理 - [x] 3.2 验证本次改动未引入公共站内通知相关功能
- [ ] 3.3 明确公共站内通知中心页面不在本次实现范围内。 - [x] 3.3 运行相关前端校验,并执行 `openspec validate update-agent-fund-low-balance-warning --strict`
## 4. Verification
- [ ] 4.1 验证现金可用余额高于、等于、低于100元三种情况下的状态展示正确且仅由 `low_balance_warning` 控制。
- [ ] 4.2 验证通知跳转到对应店铺资金页成功,无权限时进入统一错误处理。
- [ ] 4.3 运行相关前端校验,并执行 `openspec validate update-agent-fund-low-balance-warning --strict`

View File

@@ -0,0 +1,29 @@
# Change: 店铺平台业务员选择、展示与筛选
## Why
当前店铺没有记录负责的平台业务员,运营无法在新建、编辑、列表和详情中维护或查看业务归属。需要为店铺绑定可选的平台业务员,该绑定仅用于业务归属和通知关系,不承载分销、佣金或发展层级语义。
## What Changes
- 在店铺新建和编辑表单新增可清空、可搜索的“平台业务员”选择器。
- 候选业务员复用启用的平台账号查询接口,展示账号名和手机号摘要。
- 扩展店铺创建、更新、列表查询、列表返回和详情返回的数据契约,支持 `business_owner_account_id` 及业务员展示字段。
- 在店铺列表新增业务员列和按业务员筛选项,在店铺详情展示业务员名称和手机号摘要。
- 已停用的已绑定业务员仅用于历史列表和详情展示,不能作为新建或编辑表单的候选项。
- 不新增分销、佣金、发展层级或其他业务结算行为。
## Impact
- Affected specs: `shop-management`
- Affected code:
- `src/api/modules/shop.ts`
- `src/types/api/shop.ts`
- 平台账号查询 API 类型或服务模块
- `src/views/shop-management/list/index.vue`
- 店铺详情页或详情抽屉的展示配置
- API contracts:
- `POST /api/admin/shops`
- `PUT /api/admin/shops/{id}`
- `GET /api/admin/shops`
- `GET /api/admin/accounts?account_type=platform&status=1`

View File

@@ -0,0 +1,80 @@
## ADDED Requirements
### Requirement: Shop Platform Salesperson Assignment
The system SHALL allow users to assign or clear an optional platform salesperson for a shop during shop creation and editing. The assignment SHALL represent only business ownership and notification relationship and MUST NOT create distribution, commission, or hierarchy behavior.
#### Scenario: Assign an enabled platform account during shop creation
- **GIVEN** 用户进入店铺新建表单
- **WHEN** 用户在“平台业务员”可搜索下拉中选择一个启用的平台账号
- **THEN** 系统 MUST 提交该账号的 `business_owner_account_id`
- **AND** 候选项 MUST 展示账号名和手机号摘要
#### Scenario: Edit a shop salesperson assignment
- **GIVEN** 用户进入已存在店铺的编辑表单
- **WHEN** 用户选择不同的启用平台账号
- **THEN** 系统 MUST 在 `PUT /api/admin/shops/{id}` 请求中提交新的 `business_owner_account_id`
#### Scenario: Clear a shop salesperson assignment
- **GIVEN** 店铺已绑定平台业务员
- **WHEN** 用户清空“平台业务员”选择器并提交编辑表单
- **THEN** 系统 MUST 提交 `business_owner_account_id: null`
- **AND** 店铺不再关联该业务员
#### Scenario: Do not expose disabled accounts as editable candidates
- **WHEN** 用户打开或搜索“平台业务员”选择器
- **THEN** 系统 MUST 仅查询 `account_type=platform``status=1` 的账号
- **AND** 已停用平台账号 MUST NOT 作为新建或编辑候选项显示
#### Scenario: No eligible salesperson exists
- **GIVEN** 不存在启用的平台账号
- **WHEN** 用户打开“平台业务员”选择器
- **THEN** 系统 MUST 显示无可选账号状态
- **AND** 用户仍 MUST 能够不选择业务员完成表单提交
### Requirement: Shop Salesperson List Filtering And Display
The shop list SHALL display the assigned salesperson and support filtering by `business_owner_account_id`.
#### Scenario: Display salesperson in shop list
- **WHEN** 用户查看店铺列表
- **THEN** 列表 MUST 显示业务员列
- **AND** 已绑定业务员的行 MUST 展示 `business_owner_name``business_owner_phone_masked`
#### Scenario: Filter shops by salesperson
- **WHEN** 用户在店铺列表选择一个平台业务员作为筛选条件
- **THEN** 前端 MUST 在 `GET /api/admin/shops` 请求中提交该账号的 `business_owner_account_id`
- **AND** 列表 MUST 仅展示后端返回的匹配店铺
#### Scenario: Display historical disabled salesperson in list
- **GIVEN** 店铺历史上已绑定的平台业务员后来被停用
- **WHEN** 用户查看店铺列表
- **THEN** 列表 MUST 继续展示该店铺返回的 `business_owner_name``business_owner_phone_masked`
### Requirement: Shop Detail Salesperson Display
The shop detail SHALL display the assigned salesperson name and masked phone from the shop detail response, including historical assignments to disabled accounts.
#### Scenario: Display salesperson in shop detail
- **WHEN** 用户查看已绑定平台业务员的店铺详情
- **THEN** 页面 MUST 展示业务员名称和手机号摘要
#### Scenario: Display historical disabled salesperson in detail
- **GIVEN** 店铺绑定的业务员已停用
- **WHEN** 用户查看该店铺详情
- **THEN** 页面 MUST 继续使用详情响应中的 `business_owner_name``business_owner_phone_masked` 展示历史业务员信息
#### Scenario: Do not add distribution or commission semantics
- **WHEN** 用户查看店铺新建、编辑、列表或详情页面
- **THEN** 页面 MUST NOT 将平台业务员描述为分销关系、佣金关系或发展层级

View File

@@ -0,0 +1,27 @@
## 1. API Contract
- [x] 1.1 更新店铺创建和编辑请求类型,支持可空的 `business_owner_account_id`
- [x] 1.2 更新店铺列表查询类型,支持 `business_owner_account_id` 筛选参数。
- [x] 1.3 更新店铺列表和详情响应类型,读取业务员 ID、名称和脱敏手机号字段。
- [x] 1.4 复用启用平台账号查询接口,读取 `account_id``account_name``phone_masked` 作为候选项。
## 2. Shop Form UI
- [x] 2.1 在店铺新建和编辑表单增加可搜索、可清空的“平台业务员”下拉选择。
- [x] 2.2 候选项展示账号名和手机号摘要,仅查询并展示启用的平台账号。
- [x] 2.3 支持清空已选择业务员,并按 `business_owner_account_id: null` 提交。
- [x] 2.4 已停用业务员不出现在编辑候选项中。
## 3. Shop List And Detail UI
- [x] 3.1 在店铺列表增加业务员列,展示业务员名称和手机号摘要。
- [x] 3.2 在店铺列表搜索区增加按业务员筛选项,并提交选中的 `business_owner_account_id`
- [x] 3.3 在店铺详情展示业务员名称和手机号摘要;已停用但历史绑定的业务员仍正常显示。
## 4. Verification
- [ ] 4.1 验证创建、编辑、清空业务员和无可选账号状态。
- [ ] 4.2 验证列表业务员展示和按业务员筛选正确。
- [ ] 4.3 验证详情对历史停用业务员仍能展示名称和手机号摘要。
- [x] 4.4 确认页面不引入分销、佣金或发展层级文案。
- [x] 4.5 运行相关前端校验,并执行 `openspec validate update-shop-salesperson-selection --strict`

View File

@@ -6,6 +6,8 @@ import { BaseService } from '../BaseService'
import type { import type {
PlatformAccount, PlatformAccount,
AccountQueryParams, AccountQueryParams,
PlatformSalesperson,
PlatformSalespersonQueryParams,
CreatePlatformAccountParams, CreatePlatformAccountParams,
BaseResponse, BaseResponse,
PaginationResponse PaginationResponse
@@ -23,6 +25,16 @@ export class AccountService extends BaseService {
return this.getPage<PlatformAccount>('/api/admin/accounts', params) return this.getPage<PlatformAccount>('/api/admin/accounts', params)
} }
/**
* 获取启用的平台业务员候选
* GET /api/admin/accounts?account_type=platform&status=1
*/
static getPlatformSalespeople(
params: PlatformSalespersonQueryParams
): Promise<PaginationResponse<PlatformSalesperson>> {
return this.getPage<PlatformSalesperson>('/api/admin/accounts', params)
}
/** /**
* 创建账号 * 创建账号
* POST /api/admin/accounts * POST /api/admin/accounts

View File

@@ -107,6 +107,20 @@ export interface AccountQueryParams extends PaginationParams {
enterprise_id?: number // 按企业ID筛选 enterprise_id?: number // 按企业ID筛选
} }
// 平台业务员候选账号
export interface PlatformSalesperson {
account_id: number
account_name: string
phone_masked: string
}
// 平台业务员候选查询参数
export interface PlatformSalespersonQueryParams extends PaginationParams {
account_type: 'platform'
status: AccountStatus
account_name?: string
}
// 代理商查询参数 // 代理商查询参数
export interface AgentQueryParams extends PaginationParams { export interface AgentQueryParams extends PaginationParams {
keyword?: string keyword?: string

View File

@@ -211,6 +211,10 @@ export interface ShopFundSummaryItem {
phone?: string // 主账号手机号 phone?: string // 主账号手机号
main_balance: number // 预充值余额(分) main_balance: number // 预充值余额(分)
main_frozen_balance: number // 预充值冻结余额(分) main_frozen_balance: number // 预充值冻结余额(分)
balance: number // 现金余额(分)
frozen_balance: number // 现金冻结金额(分)
cash_available: number // 现金可用余额(分)
low_balance_warning: boolean // 现金可用余额不足100元预警
total_commission: number // 总佣金(分) total_commission: number // 总佣金(分)
available_commission: number // 可提现佣金(分) available_commission: number // 可提现佣金(分)
frozen_commission: number // 冻结中佣金(分) frozen_commission: number // 冻结中佣金(分)

View File

@@ -21,6 +21,9 @@ export interface ShopResponse {
contact_name: string // 联系人姓名 contact_name: string // 联系人姓名
contact_phone: string // 联系人电话 contact_phone: string // 联系人电话
status: number // 状态 (0:禁用, 1:启用) status: number // 状态 (0:禁用, 1:启用)
business_owner_account_id?: number | null // 平台业务员账号ID
business_owner_name?: string // 平台业务员名称
business_owner_phone_masked?: string // 平台业务员脱敏手机号
} }
// 店铺列表查询参数 // 店铺列表查询参数
@@ -32,6 +35,7 @@ export interface ShopQueryParams extends PaginationParams {
parent_id?: number | null // 上级店铺ID parent_id?: number | null // 上级店铺ID
level?: number | null // 店铺层级 (1-7级) level?: number | null // 店铺层级 (1-7级)
status?: number | null // 状态 (0:禁用, 1:启用) status?: number | null // 状态 (0:禁用, 1:启用)
business_owner_account_id?: number | null // 平台业务员账号ID
page?: number // 页码 page?: number // 页码
page_size?: number // 每页数量 page_size?: number // 每页数量
} }
@@ -50,6 +54,7 @@ export interface CreateShopParams {
address?: string // 详细地址 address?: string // 详细地址
contact_name?: string // 联系人姓名 contact_name?: string // 联系人姓名
contact_phone?: string // 联系人电话 contact_phone?: string // 联系人电话
business_owner_account_id?: number | null // 平台业务员账号ID
} }
// 更新店铺参数 // 更新店铺参数
@@ -62,6 +67,7 @@ export interface UpdateShopParams {
address?: string // 详细地址 address?: string // 详细地址
contact_name?: string // 联系人姓名 contact_name?: string // 联系人姓名
contact_phone?: string // 联系人电话 contact_phone?: string // 联系人电话
business_owner_account_id?: number | null // 平台业务员账号ID
} }
// 店铺列表分页响应 // 店铺列表分页响应

View File

@@ -550,7 +550,9 @@
{ label: '店铺名称', prop: 'shop_name' }, { label: '店铺名称', prop: 'shop_name' },
{ label: '用户名', prop: 'username' }, { label: '用户名', prop: 'username' },
{ label: '手机号', prop: 'phone' }, { label: '手机号', prop: 'phone' },
{ label: '预充值余额', prop: 'main_balance' }, { label: '现金余额', prop: 'balance' },
{ label: '冻结金额', prop: 'frozen_balance' },
{ label: '余额预警', prop: 'low_balance_warning' },
{ label: '总佣金', prop: 'total_commission' }, { label: '总佣金', prop: 'total_commission' },
{ label: '可提现', prop: 'available_commission' }, { label: '可提现', prop: 'available_commission' },
{ label: '冻结中', prop: 'frozen_commission' }, { label: '冻结中', prop: 'frozen_commission' },
@@ -637,17 +639,33 @@
width: 120 width: 120
}, },
{ {
prop: 'main_balance', prop: 'balance',
label: '预充值余额', label: '现金余额',
minWidth: 130, minWidth: 130,
formatter: (row: ShopFundSummaryItem) => { formatter: (row: ShopFundSummaryItem) => {
return h( return h(
'span', 'span',
{ style: 'color: var(--el-color-primary); font-weight: 500' }, { style: 'color: var(--el-color-primary); font-weight: 500' },
formatMoney(row.main_balance) formatMoney(row.balance)
) )
} }
}, },
{
prop: 'frozen_balance',
label: '冻结金额',
minWidth: 120,
formatter: (row: ShopFundSummaryItem) => formatMoney(row.frozen_balance)
},
{
prop: 'low_balance_warning',
label: '余额预警',
minWidth: 170,
formatter: (row: ShopFundSummaryItem) => {
if (!row.low_balance_warning) return '-'
return h(ElTag, { type: 'danger' }, () => '现金余额不足100元')
}
},
{ {
prop: 'total_commission', prop: 'total_commission',
label: '总佣金', label: '总佣金',

View File

@@ -126,6 +126,37 @@
</ElCol> </ElCol>
</ElRow> </ElRow>
<ElRow :gutter="20">
<ElCol :span="12">
<ElFormItem label="平台业务员">
<ElSelect
v-model="formData.business_owner_account_id"
filterable
remote
clearable
placeholder="请选择或搜索平台业务员"
:remote-method="searchPlatformSalespeople"
:loading="salespersonLoading"
style="width: 100%"
>
<ElOption
v-for="salesperson in salespersonOptions"
:key="salesperson.account_id"
:label="salesperson.account_name"
:value="salesperson.account_id"
>
<div style="display: flex; justify-content: space-between">
<span>{{ salesperson.account_name }}</span>
<span style="font-size: 12px; color: var(--el-text-color-secondary)">
{{ salesperson.phone_masked }}
</span>
</div>
</ElOption>
</ElSelect>
</ElFormItem>
</ElCol>
</ElRow>
<!-- 新增店铺时的初始账号信息 --> <!-- 新增店铺时的初始账号信息 -->
<template v-if="dialogType === 'add'"> <template v-if="dialogType === 'add'">
<div class="form-section-title"> <div class="form-section-title">
@@ -207,6 +238,16 @@
</template> </template>
</ElDialog> </ElDialog>
<ElDialog v-model="detailDialogVisible" title="店铺详情" width="500px">
<ElDescriptions v-if="detailShop" :column="1" border>
<ElDescriptionsItem label="店铺名称">{{ detailShop.shop_name }}</ElDescriptionsItem>
<ElDescriptionsItem label="店铺编号">{{ detailShop.shop_code }}</ElDescriptionsItem>
<ElDescriptionsItem label="平台业务员">
{{ formatBusinessOwner(detailShop) }}
</ElDescriptionsItem>
</ElDescriptions>
</ElDialog>
<!-- 店铺默认角色管理对话框 --> <!-- 店铺默认角色管理对话框 -->
<ElDialog <ElDialog
v-model="defaultRolesDialogVisible" v-model="defaultRolesDialogVisible"
@@ -287,10 +328,10 @@
import { useCheckedColumns } from '@/composables/useCheckedColumns' import { useCheckedColumns } from '@/composables/useCheckedColumns'
import { useAuth } from '@/composables/useAuth' import { useAuth } from '@/composables/useAuth'
import { generateShopCode } from '@/utils/codeGenerator' import { generateShopCode } from '@/utils/codeGenerator'
import { ShopService, RoleService } from '@/api/modules' import { ShopService, RoleService, AccountService } from '@/api/modules'
import type { SearchFormItem } from '@/types' import type { SearchFormItem } from '@/types'
import type { ShopResponse, ShopRoleResponse } from '@/types/api' import type { PlatformSalesperson, ShopResponse, ShopRoleResponse } from '@/types/api'
import { RoleType, RoleStatus } from '@/types/api' import { AccountStatus, RoleType, RoleStatus } from '@/types/api'
import { formatDateTime } from '@/utils/business/format' import { formatDateTime } from '@/utils/business/format'
import { CommonStatus, getStatusText, STATUS_SELECT_OPTIONS } from '@/config/constants' import { CommonStatus, getStatusText, STATUS_SELECT_OPTIONS } from '@/config/constants'
import { regionData } from '@/utils/constants/regionData' import { regionData } from '@/utils/constants/regionData'
@@ -307,6 +348,10 @@
const submitLoading = ref(false) const submitLoading = ref(false)
const defaultRoleLoading = ref(false) const defaultRoleLoading = ref(false)
const defaultRoleList = ref<any[]>([]) const defaultRoleList = ref<any[]>([])
const salespersonLoading = ref(false)
const salespersonOptions = ref<PlatformSalesperson[]>([])
const detailDialogVisible = ref(false)
const detailShop = ref<ShopResponse | null>(null)
// 级联选择相关 // 级联选择相关
const parentShopCascadeOptions = ref<any[]>([]) const parentShopCascadeOptions = ref<any[]>([])
@@ -370,6 +415,39 @@
} }
} }
const searchPlatformSalespeople = async (query: string) => {
salespersonLoading.value = true
try {
const res = await AccountService.getPlatformSalespeople({
page: 1,
page_size: 20,
account_type: 'platform',
status: AccountStatus.ENABLED,
account_name: query || undefined
})
if (res.code === 0) {
salespersonOptions.value = res.data.items || []
}
} catch (error) {
console.error('获取平台业务员列表失败:', error)
salespersonOptions.value = []
} finally {
salespersonLoading.value = false
}
}
const formatBusinessOwner = (shop: ShopResponse) => {
if (!shop.business_owner_name) return '-'
return shop.business_owner_phone_masked
? `${shop.business_owner_name} (${shop.business_owner_phone_masked})`
: shop.business_owner_name
}
const showDetail = (row: ShopResponse) => {
detailShop.value = row
detailDialogVisible.value = true
}
// 定义表单搜索初始值 // 定义表单搜索初始值
const initialSearchState = { const initialSearchState = {
shop_name: '', shop_name: '',
@@ -377,7 +455,8 @@
parent_shop_name: '', parent_shop_name: '',
parent_id: undefined as number | undefined, parent_id: undefined as number | undefined,
level: undefined as number | undefined, level: undefined as number | undefined,
status: undefined as number | undefined status: undefined as number | undefined,
business_owner_account_id: undefined as number | undefined
} }
// 响应式表单数据 // 响应式表单数据
@@ -457,6 +536,23 @@
clearable: true, clearable: true,
placeholder: '请选择状态' placeholder: '请选择状态'
} }
},
{
label: '平台业务员',
prop: 'business_owner_account_id',
type: 'select',
config: {
clearable: true,
filterable: true,
remote: true,
remoteMethod: (query: string) => searchPlatformSalespeople(query),
placeholder: '请选择或搜索平台业务员'
},
options: () =>
salespersonOptions.value.map((salesperson) => ({
label: `${salesperson.account_name} (${salesperson.phone_masked})`,
value: salesperson.account_id
}))
} }
]) ])
@@ -468,6 +564,7 @@
{ label: '所在地区', prop: 'region' }, { label: '所在地区', prop: 'region' },
{ label: '联系人', prop: 'contact_name' }, { label: '联系人', prop: 'contact_name' },
{ label: '联系电话', prop: 'contact_phone' }, { label: '联系电话', prop: 'contact_phone' },
{ label: '平台业务员', prop: 'business_owner_name' },
...(canModifyShopStatus ? [{ label: '状态', prop: 'status' }] : []), ...(canModifyShopStatus ? [{ label: '状态', prop: 'status' }] : []),
{ label: '创建时间', prop: 'created_at' }, { label: '创建时间', prop: 'created_at' },
{ label: '操作', prop: 'operation' } { label: '操作', prop: 'operation' }
@@ -497,6 +594,7 @@
formData.address = row.address || '' formData.address = row.address || ''
formData.contact_name = row.contact_name || '' formData.contact_name = row.contact_name || ''
formData.contact_phone = row.contact_phone || '' formData.contact_phone = row.contact_phone || ''
formData.business_owner_account_id = row.business_owner_account_id ?? null
formData.status = row.status formData.status = row.status
formData.init_username = '' formData.init_username = ''
formData.init_password = '' formData.init_password = ''
@@ -514,6 +612,7 @@
formData.address = '' formData.address = ''
formData.contact_name = '' formData.contact_name = ''
formData.contact_phone = '' formData.contact_phone = ''
formData.business_owner_account_id = null
formData.status = CommonStatus.ENABLED formData.status = CommonStatus.ENABLED
formData.init_username = '' formData.init_username = ''
formData.init_password = '' formData.init_password = ''
@@ -597,6 +696,13 @@
label: '联系电话', label: '联系电话',
width: 130 width: 130
}, },
{
prop: 'business_owner_name',
label: '平台业务员',
minWidth: 180,
showOverflowTooltip: true,
formatter: (row: ShopResponse) => formatBusinessOwner(row)
},
...(canModifyShopStatus ...(canModifyShopStatus
? [ ? [
{ {
@@ -630,6 +736,12 @@
const getActions = (row: ShopResponse) => { const getActions = (row: ShopResponse) => {
const actions: any[] = [] const actions: any[] = []
actions.push({
label: '详情',
handler: () => showDetail(row),
type: 'primary'
})
if (hasAuth('shop:look_customer')) { if (hasAuth('shop:look_customer')) {
actions.push({ actions.push({
label: '账号列表', label: '账号列表',
@@ -685,7 +797,8 @@
init_username: '', init_username: '',
init_password: '', init_password: '',
init_phone: '', init_phone: '',
default_role_id: undefined as number | undefined default_role_id: undefined as number | undefined,
business_owner_account_id: null as number | null
}) })
// 处理编码生成 // 处理编码生成
@@ -766,6 +879,7 @@
loadTopLevelShops() // 加载顶级店铺用于级联选择 loadTopLevelShops() // 加载顶级店铺用于级联选择
searchDefaultRoles('') // 加载初始默认角色列表 searchDefaultRoles('') // 加载初始默认角色列表
searchParentShops('') // 加载上级店铺选项 searchParentShops('') // 加载上级店铺选项
searchPlatformSalespeople('') // 加载启用的平台业务员选项
}) })
// 获取店铺列表 // 获取店铺列表
@@ -780,7 +894,8 @@
parent_shop_name: searchForm.parent_shop_name || undefined, parent_shop_name: searchForm.parent_shop_name || undefined,
parent_id: searchForm.parent_id, parent_id: searchForm.parent_id,
level: searchForm.level, level: searchForm.level,
status: searchForm.status status: searchForm.status,
business_owner_account_id: searchForm.business_owner_account_id
} }
const res = await ShopService.getShops(params) const res = await ShopService.getShops(params)
if (res.code === 0) { if (res.code === 0) {
@@ -850,7 +965,8 @@
init_username: formData.init_username, init_username: formData.init_username,
init_password: formData.init_password, init_password: formData.init_password,
init_phone: formData.init_phone, init_phone: formData.init_phone,
default_role_id: formData.default_role_id default_role_id: formData.default_role_id,
business_owner_account_id: formData.business_owner_account_id
} }
// 可选字段 - parent_id 可能是数组(级联选择器)或数字 // 可选字段 - parent_id 可能是数组(级联选择器)或数字
@@ -871,7 +987,8 @@
} else { } else {
const data: any = { const data: any = {
shop_name: formData.shop_name, shop_name: formData.shop_name,
status: formData.status status: formData.status,
business_owner_account_id: formData.business_owner_account_id
} }
// 可选字段 // 可选字段