Merge branch 'main' of https://gitlab.minzhong.cn/mz/video-gen
This commit is contained in:
@@ -191,7 +191,7 @@ const AdminBankTransactions: React.FC = () => {
|
||||
allowClear
|
||||
options={accounts.map(a => ({
|
||||
value: a.id,
|
||||
label: `${a.bank_name} - ${a.account_no} (${a.account_name})`,
|
||||
label: `${a.bankName} - ${a.accountNo} (${a.accountName})`,
|
||||
}))}
|
||||
/>
|
||||
<RangePicker
|
||||
@@ -218,7 +218,7 @@ const AdminBankTransactions: React.FC = () => {
|
||||
|
||||
{selectedAccount && (
|
||||
<div style={{ padding: '8px 12px', background: '#f8fafc', borderRadius: 8, fontSize: 13, color: '#64748b' }}>
|
||||
<strong>当前查询账户:</strong>{selectedAccount.bank_name} | {selectedAccount.account_no} | {selectedAccount.account_name}
|
||||
<strong>当前查询账户:</strong>{selectedAccount.bankName} | {selectedAccount.accountNo} | {selectedAccount.accountName}
|
||||
</div>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
@@ -18,12 +18,10 @@ import type { ScheduledTask } from '../types';
|
||||
const { TextArea } = Input;
|
||||
|
||||
const SCHEDULE_TYPE_OPTIONS = [
|
||||
{ value: 'external_api', label: '外部接口调用' },
|
||||
{ value: 'internal_method', label: '内部方法执行' },
|
||||
];
|
||||
|
||||
const TASK_TYPE_LABELS: Record<string, string> = {
|
||||
external_api: '外部接口',
|
||||
internal_method: '内部方法',
|
||||
};
|
||||
|
||||
@@ -70,15 +68,15 @@ const AdminScheduledTasks: React.FC = () => {
|
||||
}
|
||||
form.setFieldsValue({
|
||||
name: task.name,
|
||||
task_type: task.task_type,
|
||||
task_type: task.taskType,
|
||||
schedule: task.schedule,
|
||||
config: configStr,
|
||||
is_active: task.is_active,
|
||||
is_active: task.isActive,
|
||||
});
|
||||
} else {
|
||||
setEditing(null);
|
||||
form.resetFields();
|
||||
form.setFieldsValue({ is_active: true, task_type: 'external_api' });
|
||||
form.setFieldsValue({ is_active: true, task_type: 'internal_method' });
|
||||
}
|
||||
setActiveTab('basic');
|
||||
setModal(true);
|
||||
@@ -142,30 +140,28 @@ const AdminScheduledTasks: React.FC = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const taskType = Form.useWatch('task_type', form);
|
||||
|
||||
const columns = [
|
||||
{ title: '任务名称', dataIndex: 'name', width: 160, ellipsis: true },
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'task_type',
|
||||
dataIndex: 'taskType',
|
||||
width: 100,
|
||||
render: (v: string) => <Tag color={v === 'external_api' ? 'blue' : 'purple'}>{TASK_TYPE_LABELS[v] || v}</Tag>,
|
||||
render: (v: string) => <Tag color="purple">{TASK_TYPE_LABELS[v] || v}</Tag>,
|
||||
},
|
||||
{ title: '调度表达式', dataIndex: 'schedule', width: 140, render: (v: string) => <code style={{ background: '#f1f5f9', padding: '2px 6px', borderRadius: 4 }}>{v}</code> },
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'is_active',
|
||||
dataIndex: 'isActive',
|
||||
width: 80,
|
||||
render: (v: boolean) => <Tag color={v ? 'green' : 'default'}>{v ? '启用' : '禁用'}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '最后执行',
|
||||
dataIndex: 'last_run_at',
|
||||
dataIndex: 'lastRunAt',
|
||||
width: 160,
|
||||
render: (v: string, r: ScheduledTask) => {
|
||||
if (!v) return '-';
|
||||
const status = r.last_status ? STATUS_LABELS[r.last_status] : null;
|
||||
const status = r.lastStatus ? STATUS_LABELS[r.lastStatus] : null;
|
||||
return (
|
||||
<span>
|
||||
{v.includes('T') ? v.replace('T', ' ').slice(0, 19) : v}
|
||||
@@ -180,8 +176,8 @@ const AdminScheduledTasks: React.FC = () => {
|
||||
render: (_: any, record: ScheduledTask) => (
|
||||
<Space size="small">
|
||||
<Button type="link" size="small" icon={<PlayCircleOutlined />} onClick={() => handleRun(record.id)}>执行</Button>
|
||||
<Button type="link" size="small" icon={record.is_active ? <StopOutlined /> : <CheckCircleOutlined />} onClick={() => handleToggle(record)}>
|
||||
{record.is_active ? '禁用' : '启用'}
|
||||
<Button type="link" size="small" icon={record.isActive ? <StopOutlined /> : <CheckCircleOutlined />} onClick={() => handleToggle(record)}>
|
||||
{record.isActive ? '禁用' : '启用'}
|
||||
</Button>
|
||||
<Button type="link" size="small" icon={<EditOutlined />} onClick={() => openModal(record)}>编辑</Button>
|
||||
<Popconfirm title="确定删除该任务?" onConfirm={() => handleDelete(record.id)} okText="确定" cancelText="取消">
|
||||
@@ -194,9 +190,9 @@ const AdminScheduledTasks: React.FC = () => {
|
||||
|
||||
const scheduleHelp = (
|
||||
<div style={{ fontSize: 12, color: '#64748b', marginTop: 4 }}>
|
||||
<div>• 纯数字:间隔秒数(如 60 = 每 60 秒执行一次)</div>
|
||||
<div>• 纯数字:间隔秒数(如 3600 = 每小时执行一次)</div>
|
||||
<div>• Cron 表达式(5 字段):分 时 日 月 周</div>
|
||||
<div>• 示例:<code>* * * * *</code> = 每分钟 | <code>0 * * * *</code> = 每小时</div>
|
||||
<div>• 示例:<code>0 * * * *</code> = 每小时 | <code>0 2 * * *</code> = 每天凌晨2点</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -283,18 +279,11 @@ const AdminScheduledTasks: React.FC = () => {
|
||||
<Form.Item
|
||||
name="config"
|
||||
label="配置 JSON"
|
||||
extra={
|
||||
taskType === 'external_api'
|
||||
? '字段:url(地址)、method(GET/POST/PUT/DELETE)、headers(对象)、payload(对象/参数)、timeout(秒,默认 30)'
|
||||
: '字段:module(模块路径,如 app.services.xxx)、function(函数名)、args(参数数组)'
|
||||
}
|
||||
extra='module 和 function 指定调用的函数,其余字段作为参数传入。服务会自动翻页拉取全部流水:'
|
||||
>
|
||||
<TextArea
|
||||
rows={8}
|
||||
placeholder={taskType === 'external_api'
|
||||
? '{\n "url": "https://api.example.com/data",\n "method": "POST",\n "headers": {},\n "payload": {}\n}'
|
||||
: '{\n "module": "app.services.report",\n "function": "generate_daily_report",\n "args": []\n}'
|
||||
}
|
||||
rows={10}
|
||||
placeholder={'{\n "module": "app.services.bank.sync_service",\n "function": "sync_bank_transactions",\n "url": "http://ceshi.web.minzhong.cn/api/api/v1/internal/get-blank-transfer-acct-time",\n "api_key": "jixekCxm8piLFi0AlfA24bDtCKJ82bfu",\n "acct_no": "110972289710001",\n "start_date": "2026-08-01",\n "end_date": "2026-08-14"\n}'}
|
||||
style={{ fontFamily: 'monospace', fontSize: 13 }}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -63,11 +63,11 @@ const AdminSettings: React.FC = () => {
|
||||
if (account) {
|
||||
setAccountEditing(account);
|
||||
accountForm.setFieldsValue({
|
||||
account_name: account.account_name,
|
||||
bank_name: account.bank_name,
|
||||
account_no: account.account_no,
|
||||
is_active: account.is_active,
|
||||
is_default: account.is_default,
|
||||
account_name: account.accountName,
|
||||
bank_name: account.bankName,
|
||||
account_no: account.accountNo,
|
||||
is_active: account.isActive,
|
||||
is_default: account.isDefault,
|
||||
description: account.description,
|
||||
});
|
||||
} else {
|
||||
@@ -109,23 +109,23 @@ const AdminSettings: React.FC = () => {
|
||||
};
|
||||
|
||||
const accountColumns = [
|
||||
{ title: '账户名称', dataIndex: 'account_name', width: 150 },
|
||||
{ title: '开户银行', dataIndex: 'bank_name', width: 150 },
|
||||
{ title: '账户名称', dataIndex: 'accountName', width: 150 },
|
||||
{ title: '开户银行', dataIndex: 'bankName', width: 150 },
|
||||
{
|
||||
title: '银行账号',
|
||||
dataIndex: 'account_no',
|
||||
dataIndex: 'accountNo',
|
||||
width: 180,
|
||||
render: (v: string) => <code style={{ background: '#f1f5f9', padding: '2px 6px', borderRadius: 4 }}>{v}</code>,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'is_active',
|
||||
dataIndex: 'isActive',
|
||||
width: 80,
|
||||
render: (v: boolean) => <Tag color={v ? 'green' : 'default'}>{v ? '启用' : '禁用'}</Tag>,
|
||||
},
|
||||
{
|
||||
title: '默认',
|
||||
dataIndex: 'is_default',
|
||||
dataIndex: 'isDefault',
|
||||
width: 80,
|
||||
render: (v: boolean) => v && <Tag color="blue">默认</Tag>,
|
||||
},
|
||||
@@ -159,10 +159,6 @@ const AdminSettings: React.FC = () => {
|
||||
if (!data.some(c => c.key === 'single_device_login_enabled')) {
|
||||
data.push({ id: 'cfg_single_device_login_enabled', key: 'single_device_login_enabled', value: 'false', description: '启用单设备登录(同端互斥):同一设备类型只允许一个登录会话' });
|
||||
}
|
||||
// 确保 OA 地址配置存在
|
||||
if (!data.some(c => c.key === 'oa_url')) {
|
||||
data.push({ id: 'cfg_oa_url', key: 'oa_url', value: '', description: 'OA系统地址' });
|
||||
}
|
||||
setConfigs(data);
|
||||
const formValues: Record<string, any> = {};
|
||||
data.forEach(c => { formValues[c.key] = c.value; });
|
||||
@@ -317,7 +313,7 @@ const AdminSettings: React.FC = () => {
|
||||
'协议配置': configs.filter(c => c.key === 'user_agreement_privacy_url'),
|
||||
'SEO 设置': configs.filter(c => c.key.startsWith('seo_')),
|
||||
'用户积分配置': configs.filter(c => c.key.startsWith('user_') && c.key.includes('credits')),
|
||||
'系统对接': configs.filter(c => c.key.startsWith('oa_')),
|
||||
'收款银行': [],
|
||||
'其他配置': configs.filter(c => c.key === 'operation_manual'),
|
||||
};
|
||||
|
||||
@@ -530,33 +526,15 @@ const AdminSettings: React.FC = () => {
|
||||
},
|
||||
{
|
||||
key: 'integration',
|
||||
label: '系统对接',
|
||||
label: '收款银行',
|
||||
children: (
|
||||
<div>
|
||||
<Form form={form} layout="vertical">
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<Typography.Text strong style={{ fontSize: 14, display: 'block', marginBottom: 12, paddingBottom: 8, borderBottom: '1px solid #f0f0f5' }}>
|
||||
系统对接
|
||||
</Typography.Text>
|
||||
{groupedConfigs['系统对接']?.map(config => (
|
||||
<Form.Item
|
||||
key={config.id}
|
||||
name={config.key}
|
||||
label={<span style={{ fontWeight: 500 }}>{config.description}</span>}
|
||||
extra={getFieldDescription(config)}
|
||||
>
|
||||
{getFieldComponent(config)}
|
||||
</Form.Item>
|
||||
))}
|
||||
</div>
|
||||
</Form>
|
||||
|
||||
{/* 银行账户管理 */}
|
||||
<div style={{ marginBottom: 24 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 12, paddingBottom: 8, borderBottom: '1px solid #f0f0f5' }}>
|
||||
<Typography.Text strong style={{ fontSize: 14 }}>
|
||||
<BankOutlined style={{ marginRight: 8, color: '#6366f1' }} />
|
||||
银行账户管理
|
||||
收款银行管理
|
||||
</Typography.Text>
|
||||
<Button type="primary" icon={<PlusOutlined />} size="small" onClick={() => openAccountModal()}>
|
||||
新增账户
|
||||
@@ -710,7 +688,7 @@ const AdminSettings: React.FC = () => {
|
||||
];
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 720 }}>
|
||||
<div style={{ maxWidth: 1080 }}>
|
||||
<Card variant="outlined" style={{ borderRadius: 12, border: '1px solid #f0f0f5', marginBottom: 16 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 24 }}>
|
||||
<div style={{
|
||||
|
||||
@@ -1645,14 +1645,14 @@ export interface InvoiceDetail {
|
||||
|
||||
export interface BankAccount {
|
||||
id: string;
|
||||
account_name: string;
|
||||
bank_name: string;
|
||||
account_no: string;
|
||||
is_active: boolean;
|
||||
is_default: boolean;
|
||||
accountName: string;
|
||||
bankName: string;
|
||||
accountNo: string;
|
||||
isActive: boolean;
|
||||
isDefault: boolean;
|
||||
description?: string | null;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
|
||||
// ── Scheduled Task ─────────────────────────────────────
|
||||
@@ -1660,14 +1660,14 @@ export interface BankAccount {
|
||||
export interface ScheduledTask {
|
||||
id: string;
|
||||
name: string;
|
||||
task_type: 'external_api' | 'internal_method';
|
||||
taskType: 'external_api' | 'internal_method';
|
||||
schedule: string;
|
||||
config?: string | null;
|
||||
is_active: boolean;
|
||||
last_run_at?: string | null;
|
||||
last_status?: string | null;
|
||||
last_error?: string | null;
|
||||
created_by?: string | null;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
isActive: boolean;
|
||||
lastRunAt?: string | null;
|
||||
lastStatus?: string | null;
|
||||
lastError?: string | null;
|
||||
createdBy?: string | null;
|
||||
createdAt?: string | null;
|
||||
updatedAt?: string | null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user