优化商户管理, 增加商户名重置密码等等
This commit is contained in:
@@ -19,6 +19,7 @@ import type {
|
||||
Merchant,
|
||||
MerchantMember,
|
||||
MerchantMemberGroup,
|
||||
MerchantOwnerAccount,
|
||||
MerchantRole,
|
||||
MerchantProduct,
|
||||
PageResult,
|
||||
@@ -217,6 +218,10 @@ export const platformApi = {
|
||||
request.post(`/platform/merchants/${merchantId}/products/assign`, { catalog_ids: catalogIds }).then((r) => r.data.data as { assigned: number }),
|
||||
wallet: (merchantId: number) =>
|
||||
request.get(`/platform/merchants/${merchantId}/wallet`).then((r) => r.data.data as WalletAccount),
|
||||
merchantAccount: (merchantId: number) =>
|
||||
request.get(`/platform/merchants/${merchantId}/account`).then((r) => r.data.data as MerchantOwnerAccount[]),
|
||||
updateMerchantAccount: (merchantId: number, data: { user_id: number; username: string; nickname?: string; password?: string; status?: number }) =>
|
||||
request.patch(`/platform/merchants/${merchantId}/account`, data).then((r) => r.data.data as MerchantOwnerAccount),
|
||||
walletLedger: (merchantId: number, params?: Record<string, unknown>) =>
|
||||
request.get(`/platform/merchants/${merchantId}/wallet/ledger`, { params }).then((r) => r.data.data as PageResult<WalletLedgerEntry>),
|
||||
adjustWallet: (merchantId: number, data: { amount: number; idempotency_key: string; note?: string }) =>
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useNavigate } from 'react-router-dom'
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Divider,
|
||||
Form,
|
||||
Input,
|
||||
InputNumber,
|
||||
@@ -10,15 +11,16 @@ import {
|
||||
Select,
|
||||
Space,
|
||||
Spin,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
Typography,
|
||||
message,
|
||||
} from 'antd'
|
||||
import { GiftOutlined, PlusOutlined, ReloadOutlined, WalletOutlined } from '@ant-design/icons'
|
||||
import { GiftOutlined, PlusOutlined, ReloadOutlined, UserOutlined, WalletOutlined } from '@ant-design/icons'
|
||||
import type { ColumnsType } from 'antd/es/table'
|
||||
import { platformApi } from '../api'
|
||||
import type { Merchant, PageResult, ProductCatalogItem, WalletAccount } from '../types'
|
||||
import type { Merchant, MerchantOwnerAccount, PageResult, ProductCatalogItem, WalletAccount } from '../types'
|
||||
import { formatDateTime } from '../utils/time'
|
||||
import { PageHeader } from '../components/PageHeader'
|
||||
|
||||
@@ -50,10 +52,16 @@ export default function PlatformMerchants() {
|
||||
const [editProduct, setEditProduct] = useState<ProductCatalogItem | null>(null)
|
||||
const [editProductOpen, setEditProductOpen] = useState(false)
|
||||
const [editProductSubmitting, setEditProductSubmitting] = useState(false)
|
||||
const [accounts, setAccounts] = useState<MerchantOwnerAccount[]>([])
|
||||
const [accountsLoading, setAccountsLoading] = useState(false)
|
||||
const [accountModalOpen, setAccountModalOpen] = useState(false)
|
||||
const [editingAccount, setEditingAccount] = useState<MerchantOwnerAccount | null>(null)
|
||||
const [accountSubmitting, setAccountSubmitting] = useState(false)
|
||||
const [createForm] = Form.useForm()
|
||||
const [settingsForm] = Form.useForm()
|
||||
const [adjustForm] = Form.useForm()
|
||||
const [editProductForm] = Form.useForm()
|
||||
const [accountForm] = Form.useForm()
|
||||
|
||||
const load = useCallback(async (page = data.page, size = data.size) => {
|
||||
setLoading(true)
|
||||
@@ -112,6 +120,52 @@ export default function PlatformMerchants() {
|
||||
}
|
||||
}
|
||||
|
||||
const loadAccounts = useCallback(async (merchantId: number) => {
|
||||
setAccountsLoading(true)
|
||||
try {
|
||||
setAccounts(await platformApi.merchantAccount(merchantId))
|
||||
} catch (e) {
|
||||
message.error(e instanceof Error ? e.message : '加载商户账号失败')
|
||||
setAccounts([])
|
||||
} finally {
|
||||
setAccountsLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const openAccountEdit = (account: MerchantOwnerAccount) => {
|
||||
setEditingAccount(account)
|
||||
accountForm.resetFields()
|
||||
accountForm.setFieldsValue({
|
||||
username: account.username,
|
||||
nickname: account.nickname,
|
||||
password: '',
|
||||
status: account.status === 1,
|
||||
})
|
||||
setAccountModalOpen(true)
|
||||
}
|
||||
|
||||
const submitAccount = async () => {
|
||||
if (!selectedMerchant || !editingAccount) return
|
||||
const values = await accountForm.validateFields()
|
||||
setAccountSubmitting(true)
|
||||
try {
|
||||
await platformApi.updateMerchantAccount(selectedMerchant.id, {
|
||||
user_id: editingAccount.user_id,
|
||||
username: values.username,
|
||||
nickname: values.nickname || '',
|
||||
password: values.password || undefined,
|
||||
status: values.status ? 1 : 0,
|
||||
})
|
||||
message.success(values.password ? '商户账号已更新,密码已重置' : '商户账号已更新')
|
||||
setAccountModalOpen(false)
|
||||
void loadAccounts(selectedMerchant.id)
|
||||
} catch (e) {
|
||||
message.error(e instanceof Error ? e.message : '保存失败')
|
||||
} finally {
|
||||
setAccountSubmitting(false)
|
||||
}
|
||||
}
|
||||
|
||||
const openAdjust = async (record: Merchant) => {
|
||||
setSelectedMerchant(record)
|
||||
setAdjustOpen(true)
|
||||
@@ -308,6 +362,7 @@ export default function PlatformMerchants() {
|
||||
fee_fixed_amount: record.fee_fixed_amount,
|
||||
})
|
||||
setSettingsOpen(true)
|
||||
void loadAccounts(record.id)
|
||||
}}
|
||||
>
|
||||
设置
|
||||
@@ -467,6 +522,85 @@ export default function PlatformMerchants() {
|
||||
}
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<Divider plain style={{ margin: '8px 0 16px' }}>
|
||||
<Space size={6}>
|
||||
<UserOutlined style={{ color: '#64748b' }} />
|
||||
商户账号
|
||||
</Space>
|
||||
</Divider>
|
||||
<Spin spinning={accountsLoading}>
|
||||
{accounts.length === 0 ? (
|
||||
<Typography.Text type="secondary">该商户暂无负责人登录账号</Typography.Text>
|
||||
) : (
|
||||
<Table
|
||||
rowKey="user_id"
|
||||
size="small"
|
||||
pagination={false}
|
||||
dataSource={accounts}
|
||||
columns={[
|
||||
{ title: '登录用户名', dataIndex: 'username', render: (v) => <Typography.Text strong>{v}</Typography.Text> },
|
||||
{ title: '昵称', dataIndex: 'nickname', render: (v) => v || '-' },
|
||||
{
|
||||
title: '账号状态',
|
||||
dataIndex: 'status',
|
||||
width: 90,
|
||||
render: (v) => (v === 1 ? <Tag color="green">启用</Tag> : <Tag>停用</Tag>),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 70,
|
||||
render: (_, record: MerchantOwnerAccount) => (
|
||||
<Button type="link" size="small" onClick={() => openAccountEdit(record)}>编辑</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Spin>
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginTop: 8, fontSize: 12.5 }}>
|
||||
商户账号是负责人登录商户后台的凭证:可修改登录用户名与昵称、重置密码、启用或停用账号(至少保留一个启用的负责人账号)。停用账号仅影响登录,不等于停用商户;重置密码后旧密码立即失效。
|
||||
</Typography.Text>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
title={editingAccount ? `编辑商户账号:${editingAccount.username}` : '编辑商户账号'}
|
||||
open={accountModalOpen}
|
||||
onOk={submitAccount}
|
||||
onCancel={() => setAccountModalOpen(false)}
|
||||
destroyOnClose
|
||||
width={520}
|
||||
confirmLoading={accountSubmitting}
|
||||
okText="保存"
|
||||
>
|
||||
<Form form={accountForm} layout="vertical" style={{ marginTop: 16 }}>
|
||||
<Form.Item
|
||||
name="username"
|
||||
label="登录用户名"
|
||||
rules={[
|
||||
{ required: true, message: '请输入用户名' },
|
||||
{ min: 3, message: '用户名至少 3 位' },
|
||||
{ max: 64, message: '用户名不能超过 64 个字符' },
|
||||
]}
|
||||
>
|
||||
<Input autoComplete="off" placeholder="修改后负责人需使用新用户名登录" />
|
||||
</Form.Item>
|
||||
<Form.Item name="nickname" label="昵称" rules={[{ max: 64, message: '昵称不能超过 64 个字符' }]}>
|
||||
<Input autoComplete="off" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="password"
|
||||
label="重置密码"
|
||||
rules={[{ min: 6, message: '密码至少 6 位' }]}
|
||||
extra="留空表示不修改密码"
|
||||
>
|
||||
<Input.Password autoComplete="new-password" placeholder="留空不修改" />
|
||||
</Form.Item>
|
||||
<Form.Item name="status" label="账号状态" valuePropName="checked" extra="停用后该负责人将无法登录商户后台">
|
||||
<Switch checkedChildren="启用" unCheckedChildren="停用" />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
|
||||
@@ -39,6 +39,15 @@ export interface MerchantMemberGroup {
|
||||
members: MerchantMember[]
|
||||
}
|
||||
|
||||
export interface MerchantOwnerAccount {
|
||||
user_id: number
|
||||
member_id: number
|
||||
username: string
|
||||
nickname: string
|
||||
status: number
|
||||
is_default: boolean
|
||||
}
|
||||
|
||||
export interface MerchantRole {
|
||||
id: number
|
||||
merchant_id: number
|
||||
|
||||
Reference in New Issue
Block a user