|
|
@@ -6,7 +6,6 @@ import { useForm } from 'react-hook-form';
|
|
|
import { z } from 'zod';
|
|
|
import {
|
|
|
Settings,
|
|
|
- Save,
|
|
|
RefreshCw,
|
|
|
AlertCircle,
|
|
|
CheckCircle,
|
|
|
@@ -14,16 +13,14 @@ import {
|
|
|
Clock,
|
|
|
Repeat,
|
|
|
Timer,
|
|
|
- FileText,
|
|
|
- Truck
|
|
|
+ FileText
|
|
|
} from 'lucide-react';
|
|
|
|
|
|
import {
|
|
|
FeieConfig,
|
|
|
ConfigKey,
|
|
|
ConfigType,
|
|
|
- UpdateConfigRequest,
|
|
|
- ConfigListResponse
|
|
|
+ UpdateConfigRequest
|
|
|
} from '../types/feiePrinter';
|
|
|
import { createFeiePrinterClient } from '../api/feiePrinterClient';
|
|
|
import {
|
|
|
@@ -46,10 +43,8 @@ import {
|
|
|
DialogFooter,
|
|
|
DialogHeader,
|
|
|
DialogTitle,
|
|
|
- DialogTrigger,
|
|
|
Form,
|
|
|
FormControl,
|
|
|
- FormDescription,
|
|
|
FormField,
|
|
|
FormItem,
|
|
|
FormLabel,
|
|
|
@@ -61,11 +56,7 @@ import {
|
|
|
SelectTrigger,
|
|
|
SelectValue,
|
|
|
Switch,
|
|
|
- Textarea,
|
|
|
- Tabs,
|
|
|
- TabsContent,
|
|
|
- TabsList,
|
|
|
- TabsTrigger
|
|
|
+ Textarea
|
|
|
} from '@d8d/shared-ui-components';
|
|
|
|
|
|
// 配置分组
|
|
|
@@ -209,19 +200,6 @@ const configDefinitions: ConfigItemDefinition[] = [
|
|
|
}
|
|
|
];
|
|
|
|
|
|
-// 配置分组标签映射
|
|
|
-const groupLabelMap: Record<ConfigGroup, string> = {
|
|
|
- [ConfigGroup.BASIC]: '基础配置',
|
|
|
- [ConfigGroup.PRINT_POLICY]: '打印策略',
|
|
|
- [ConfigGroup.TEMPLATE]: '模板配置'
|
|
|
-};
|
|
|
-
|
|
|
-// 配置分组图标映射
|
|
|
-const groupIconMap: Record<ConfigGroup, React.ReactNode> = {
|
|
|
- [ConfigGroup.BASIC]: <Settings className="h-4 w-4" />,
|
|
|
- [ConfigGroup.PRINT_POLICY]: <Timer className="h-4 w-4" />,
|
|
|
- [ConfigGroup.TEMPLATE]: <FileText className="h-4 w-4" />
|
|
|
-};
|
|
|
|
|
|
// 配置类型标签映射
|
|
|
const typeLabelMap: Record<ConfigType, string> = {
|
|
|
@@ -298,16 +276,19 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
} = useQuery({
|
|
|
queryKey: ['printConfigs', tenantId],
|
|
|
queryFn: () => feieClient.getPrintConfigs(),
|
|
|
- enabled: !!tenantId,
|
|
|
- onSuccess: (data) => {
|
|
|
- // 将配置列表转换为映射
|
|
|
+ enabled: !!tenantId
|
|
|
+ });
|
|
|
+
|
|
|
+ // 当配置数据加载成功后,更新配置映射
|
|
|
+ useEffect(() => {
|
|
|
+ if (configList?.data) {
|
|
|
const map: Record<string, FeieConfig> = {};
|
|
|
- data.data.forEach((config) => {
|
|
|
+ configList.data.forEach((config: FeieConfig) => {
|
|
|
map[config.configKey] = config;
|
|
|
});
|
|
|
setConfigMap(map);
|
|
|
}
|
|
|
- });
|
|
|
+ }, [configList]);
|
|
|
|
|
|
// 更新配置表单
|
|
|
const updateForm = useForm<UpdateConfigFormValues>({
|
|
|
@@ -317,7 +298,7 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 更新配置Mutation
|
|
|
+ // 更新配置Mutation(用于编辑对话框)
|
|
|
const updateConfigMutation = useMutation({
|
|
|
mutationFn: ({ configKey, data }: { configKey: string; data: UpdateConfigRequest }) =>
|
|
|
feieClient.updatePrintConfig(configKey, data),
|
|
|
@@ -332,23 +313,19 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 批量更新配置Mutation
|
|
|
- const batchUpdateConfigMutation = useMutation({
|
|
|
- mutationFn: async (updates: Array<{ configKey: string; data: UpdateConfigRequest }>) => {
|
|
|
- const promises = updates.map(({ configKey, data }) =>
|
|
|
- feieClient.updatePrintConfig(configKey, data)
|
|
|
- );
|
|
|
- await Promise.all(promises);
|
|
|
- },
|
|
|
+ // 即时保存配置Mutation(用于直接编辑)
|
|
|
+ const instantSaveConfigMutation = useMutation({
|
|
|
+ mutationFn: ({ configKey, configValue }: { configKey: string; configValue: string }) =>
|
|
|
+ feieClient.updatePrintConfig(configKey, { configValue }),
|
|
|
onSuccess: () => {
|
|
|
- toast.success('批量更新配置成功');
|
|
|
queryClient.invalidateQueries({ queryKey: ['printConfigs'] });
|
|
|
},
|
|
|
onError: (error: Error) => {
|
|
|
- toast.error(`批量更新配置失败: ${error.message}`);
|
|
|
+ toast.error(`保存配置失败: ${error.message}`);
|
|
|
}
|
|
|
});
|
|
|
|
|
|
+
|
|
|
// 重置配置Mutation
|
|
|
const resetConfigMutation = useMutation({
|
|
|
mutationFn: async (configKey: string) => {
|
|
|
@@ -386,25 +363,11 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
setIsEditDialogOpen(true);
|
|
|
};
|
|
|
|
|
|
- // 处理批量保存
|
|
|
+ // 处理批量保存(现在主要用于强制刷新)
|
|
|
const handleBatchSave = () => {
|
|
|
- const updates: Array<{ configKey: string; data: UpdateConfigRequest }> = [];
|
|
|
-
|
|
|
- configDefinitions.forEach(definition => {
|
|
|
- const currentConfig = configMap[definition.key];
|
|
|
- if (currentConfig) {
|
|
|
- // 这里可以添加逻辑来获取修改后的值
|
|
|
- // 目前只是保存当前值
|
|
|
- updates.push({
|
|
|
- configKey: definition.key,
|
|
|
- data: { configValue: currentConfig.configValue }
|
|
|
- });
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- if (updates.length > 0) {
|
|
|
- batchUpdateConfigMutation.mutate(updates);
|
|
|
- }
|
|
|
+ // 由于现在有即时保存,批量保存主要用于刷新数据
|
|
|
+ toast.info('配置已实时保存,正在刷新数据...');
|
|
|
+ refetch();
|
|
|
};
|
|
|
|
|
|
// 处理重置配置
|
|
|
@@ -428,10 +391,6 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
return config.configValue;
|
|
|
};
|
|
|
|
|
|
- // 获取当前分组的配置定义
|
|
|
- const currentGroupDefinitions = configDefinitions.filter(
|
|
|
- def => def.group === activeGroup
|
|
|
- );
|
|
|
|
|
|
// 获取配置值
|
|
|
const getConfigValue = (configKey: string): string => {
|
|
|
@@ -476,37 +435,87 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
<Switch
|
|
|
checked={value === 'true'}
|
|
|
onCheckedChange={(checked) => {
|
|
|
- // 这里可以添加即时保存逻辑
|
|
|
- const newConfig = { ...config, configValue: checked.toString() };
|
|
|
+ const newValue = checked.toString();
|
|
|
+ // 更新本地状态
|
|
|
+ const newConfig = { ...config, configValue: newValue };
|
|
|
setConfigMap(prev => ({ ...prev, [definition.key]: newConfig }));
|
|
|
+ // 即时保存到服务器
|
|
|
+ instantSaveConfigMutation.mutate({
|
|
|
+ configKey: definition.key,
|
|
|
+ configValue: newValue
|
|
|
+ });
|
|
|
}}
|
|
|
+ disabled={instantSaveConfigMutation.isPending}
|
|
|
/>
|
|
|
<span className="text-sm">{value === 'true' ? '已启用' : '已禁用'}</span>
|
|
|
+ {instantSaveConfigMutation.isPending && (
|
|
|
+ <RefreshCw className="h-3 w-3 animate-spin text-muted-foreground" />
|
|
|
+ )}
|
|
|
</div>
|
|
|
) : definition.component === 'textarea' ? (
|
|
|
- <Textarea
|
|
|
- value={value}
|
|
|
- onChange={(e) => {
|
|
|
- const newConfig = { ...config, configValue: e.target.value };
|
|
|
- setConfigMap(prev => ({ ...prev, [definition.key]: newConfig }));
|
|
|
- }}
|
|
|
- rows={6}
|
|
|
- className="font-mono text-sm"
|
|
|
- />
|
|
|
- ) : definition.component === 'number' ? (
|
|
|
- <div className="flex items-center space-x-2">
|
|
|
- <Input
|
|
|
- type="number"
|
|
|
+ <div className="relative">
|
|
|
+ <Textarea
|
|
|
value={value}
|
|
|
onChange={(e) => {
|
|
|
- const newConfig = { ...config, configValue: e.target.value };
|
|
|
+ const newValue = e.target.value;
|
|
|
+ // 更新本地状态
|
|
|
+ const newConfig = { ...config, configValue: newValue };
|
|
|
setConfigMap(prev => ({ ...prev, [definition.key]: newConfig }));
|
|
|
}}
|
|
|
- min={definition.min}
|
|
|
- max={definition.max}
|
|
|
- step={definition.step}
|
|
|
- className="w-32"
|
|
|
+ onBlur={(e) => {
|
|
|
+ // 失去焦点时保存
|
|
|
+ const newValue = e.target.value;
|
|
|
+ if (newValue !== value) {
|
|
|
+ instantSaveConfigMutation.mutate({
|
|
|
+ configKey: definition.key,
|
|
|
+ configValue: newValue
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ rows={6}
|
|
|
+ className="font-mono text-sm"
|
|
|
+ disabled={instantSaveConfigMutation.isPending}
|
|
|
/>
|
|
|
+ {instantSaveConfigMutation.isPending && (
|
|
|
+ <div className="absolute right-2 top-2">
|
|
|
+ <RefreshCw className="h-3 w-3 animate-spin text-muted-foreground" />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ ) : definition.component === 'number' ? (
|
|
|
+ <div className="flex items-center space-x-2">
|
|
|
+ <div className="relative">
|
|
|
+ <Input
|
|
|
+ type="number"
|
|
|
+ value={value}
|
|
|
+ onChange={(e) => {
|
|
|
+ const newValue = e.target.value;
|
|
|
+ // 更新本地状态
|
|
|
+ const newConfig = { ...config, configValue: newValue };
|
|
|
+ setConfigMap(prev => ({ ...prev, [definition.key]: newConfig }));
|
|
|
+ }}
|
|
|
+ onBlur={(e) => {
|
|
|
+ // 失去焦点时保存
|
|
|
+ const newValue = e.target.value;
|
|
|
+ if (newValue !== value) {
|
|
|
+ instantSaveConfigMutation.mutate({
|
|
|
+ configKey: definition.key,
|
|
|
+ configValue: newValue
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ min={definition.min}
|
|
|
+ max={definition.max}
|
|
|
+ step={definition.step}
|
|
|
+ className="w-32"
|
|
|
+ disabled={instantSaveConfigMutation.isPending}
|
|
|
+ />
|
|
|
+ {instantSaveConfigMutation.isPending && (
|
|
|
+ <div className="absolute right-2 top-2">
|
|
|
+ <RefreshCw className="h-3 w-3 animate-spin text-muted-foreground" />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
{definition.key === ConfigKey.ANTI_REFUND_DELAY && (
|
|
|
<span className="text-sm text-muted-foreground">
|
|
|
({Math.floor(parseInt(value) / 60)}分{parseInt(value) % 60}秒)
|
|
|
@@ -514,14 +523,34 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
)}
|
|
|
</div>
|
|
|
) : (
|
|
|
- <Input
|
|
|
- value={value}
|
|
|
- onChange={(e) => {
|
|
|
- const newConfig = { ...config, configValue: e.target.value };
|
|
|
- setConfigMap(prev => ({ ...prev, [definition.key]: newConfig }));
|
|
|
- }}
|
|
|
- placeholder={`请输入${definition.label}`}
|
|
|
- />
|
|
|
+ <div className="relative">
|
|
|
+ <Input
|
|
|
+ value={value}
|
|
|
+ onChange={(e) => {
|
|
|
+ const newValue = e.target.value;
|
|
|
+ // 更新本地状态
|
|
|
+ const newConfig = { ...config, configValue: newValue };
|
|
|
+ setConfigMap(prev => ({ ...prev, [definition.key]: newConfig }));
|
|
|
+ }}
|
|
|
+ onBlur={(e) => {
|
|
|
+ // 失去焦点时保存
|
|
|
+ const newValue = e.target.value;
|
|
|
+ if (newValue !== value) {
|
|
|
+ instantSaveConfigMutation.mutate({
|
|
|
+ configKey: definition.key,
|
|
|
+ configValue: newValue
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ placeholder={`请输入${definition.label}`}
|
|
|
+ disabled={instantSaveConfigMutation.isPending}
|
|
|
+ />
|
|
|
+ {instantSaveConfigMutation.isPending && (
|
|
|
+ <div className="absolute right-2 top-2">
|
|
|
+ <RefreshCw className="h-3 w-3 animate-spin text-muted-foreground" />
|
|
|
+ </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
)}
|
|
|
|
|
|
{definition.key === ConfigKey.DEFAULT_PRINTER_SN && value && (
|
|
|
@@ -554,176 +583,187 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
</Button>
|
|
|
<Button
|
|
|
onClick={handleBatchSave}
|
|
|
- disabled={batchUpdateConfigMutation.isPending || isLoading}
|
|
|
+ disabled={isLoading}
|
|
|
+ variant="outline"
|
|
|
>
|
|
|
- <Save className="mr-2 h-4 w-4" />
|
|
|
- {batchUpdateConfigMutation.isPending ? '保存中...' : '保存所有更改'}
|
|
|
+ <RefreshCw className={`mr-2 h-4 w-4 ${isLoading ? 'animate-spin' : ''}`} />
|
|
|
+ 刷新数据
|
|
|
</Button>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
- {/* 配置分组标签 */}
|
|
|
- <Tabs value={activeGroup} onValueChange={(value) => setActiveGroup(value as ConfigGroup)}>
|
|
|
- <TabsList className="grid w-full grid-cols-3">
|
|
|
- <TabsTrigger value={ConfigGroup.BASIC}>
|
|
|
- <Settings className="mr-2 h-4 w-4" />
|
|
|
- 基础配置
|
|
|
- </TabsTrigger>
|
|
|
- <TabsTrigger value={ConfigGroup.PRINT_POLICY}>
|
|
|
- <Timer className="mr-2 h-4 w-4" />
|
|
|
- 打印策略
|
|
|
- </TabsTrigger>
|
|
|
- <TabsTrigger value={ConfigGroup.TEMPLATE}>
|
|
|
- <FileText className="mr-2 h-4 w-4" />
|
|
|
- 模板配置
|
|
|
- </TabsTrigger>
|
|
|
- </TabsList>
|
|
|
-
|
|
|
- {/* 加载状态 */}
|
|
|
- {isLoading ? (
|
|
|
- <Card className="mt-4">
|
|
|
- <CardContent className="pt-6">
|
|
|
- <div className="flex items-center justify-center py-8">
|
|
|
- <div className="text-center">
|
|
|
- <RefreshCw className="h-8 w-8 animate-spin mx-auto text-muted-foreground" />
|
|
|
- <p className="mt-2 text-sm text-muted-foreground">加载配置中...</p>
|
|
|
- </div>
|
|
|
+ {/* 配置分组按钮 */}
|
|
|
+ <div className="flex space-x-2 mb-4">
|
|
|
+ <Button
|
|
|
+ variant={activeGroup === ConfigGroup.BASIC ? "default" : "outline"}
|
|
|
+ onClick={() => setActiveGroup(ConfigGroup.BASIC)}
|
|
|
+ className="flex items-center"
|
|
|
+ >
|
|
|
+ <Settings className="mr-2 h-4 w-4" />
|
|
|
+ 基础配置
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant={activeGroup === ConfigGroup.PRINT_POLICY ? "default" : "outline"}
|
|
|
+ onClick={() => setActiveGroup(ConfigGroup.PRINT_POLICY)}
|
|
|
+ className="flex items-center"
|
|
|
+ >
|
|
|
+ <Timer className="mr-2 h-4 w-4" />
|
|
|
+ 打印策略
|
|
|
+ </Button>
|
|
|
+ <Button
|
|
|
+ variant={activeGroup === ConfigGroup.TEMPLATE ? "default" : "outline"}
|
|
|
+ onClick={() => setActiveGroup(ConfigGroup.TEMPLATE)}
|
|
|
+ className="flex items-center"
|
|
|
+ >
|
|
|
+ <FileText className="mr-2 h-4 w-4" />
|
|
|
+ 模板配置
|
|
|
+ </Button>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ {/* 加载状态 */}
|
|
|
+ {isLoading ? (
|
|
|
+ <Card className="mt-4">
|
|
|
+ <CardContent className="pt-6">
|
|
|
+ <div className="flex items-center justify-center py-8">
|
|
|
+ <div className="text-center">
|
|
|
+ <RefreshCw className="h-8 w-8 animate-spin mx-auto text-muted-foreground" />
|
|
|
+ <p className="mt-2 text-sm text-muted-foreground">加载配置中...</p>
|
|
|
</div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- ) : isError ? (
|
|
|
- <Card className="mt-4">
|
|
|
- <CardContent className="pt-6">
|
|
|
- <div className="flex items-center justify-center py-8">
|
|
|
- <div className="text-center">
|
|
|
- <AlertCircle className="h-8 w-8 mx-auto text-red-500" />
|
|
|
- <p className="mt-2 text-sm text-red-600">加载配置失败</p>
|
|
|
- <Button
|
|
|
- variant="outline"
|
|
|
- size="sm"
|
|
|
- className="mt-4"
|
|
|
- onClick={() => refetch()}
|
|
|
- >
|
|
|
- 重试
|
|
|
- </Button>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ ) : isError ? (
|
|
|
+ <Card className="mt-4">
|
|
|
+ <CardContent className="pt-6">
|
|
|
+ <div className="flex items-center justify-center py-8">
|
|
|
+ <div className="text-center">
|
|
|
+ <AlertCircle className="h-8 w-8 mx-auto text-red-500" />
|
|
|
+ <p className="mt-2 text-sm text-red-600">加载配置失败</p>
|
|
|
+ <Button
|
|
|
+ variant="outline"
|
|
|
+ size="sm"
|
|
|
+ className="mt-4"
|
|
|
+ onClick={() => refetch()}
|
|
|
+ >
|
|
|
+ 重试
|
|
|
+ </Button>
|
|
|
</div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- ) : (
|
|
|
- <>
|
|
|
- {/* 基础配置 */}
|
|
|
- <TabsContent value={ConfigGroup.BASIC} className="space-y-4">
|
|
|
- <Card>
|
|
|
- <CardHeader>
|
|
|
- <CardTitle>基础配置</CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 配置飞鹅打印的基础功能,包括启用状态和默认打印机
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent className="space-y-6">
|
|
|
- {configDefinitions
|
|
|
- .filter(def => def.group === ConfigGroup.BASIC)
|
|
|
- .map(renderConfigItem)}
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- </TabsContent>
|
|
|
-
|
|
|
- {/* 打印策略 */}
|
|
|
- <TabsContent value={ConfigGroup.PRINT_POLICY} className="space-y-4">
|
|
|
- <Card>
|
|
|
- <CardHeader>
|
|
|
- <CardTitle>打印策略</CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 配置打印任务的触发条件、重试策略和超时设置
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent className="space-y-6">
|
|
|
- {configDefinitions
|
|
|
- .filter(def => def.group === ConfigGroup.PRINT_POLICY)
|
|
|
- .map(renderConfigItem)}
|
|
|
-
|
|
|
- {/* 策略说明 */}
|
|
|
- <div className="rounded-lg border bg-muted/50 p-4">
|
|
|
- <h4 className="font-medium mb-2">策略说明</h4>
|
|
|
- <ul className="text-sm text-muted-foreground space-y-1">
|
|
|
- <li className="flex items-start">
|
|
|
- <Clock className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
|
|
- <span>
|
|
|
- <strong>防退款延迟</strong>: 支付成功后等待指定时间确认无退款再打印,避免无效打印
|
|
|
- </span>
|
|
|
- </li>
|
|
|
- <li className="flex items-start">
|
|
|
- <Repeat className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
|
|
- <span>
|
|
|
- <strong>重试机制</strong>: 打印失败时自动重试,最多重试指定次数,每次间隔指定时间
|
|
|
- </span>
|
|
|
- </li>
|
|
|
- <li className="flex items-start">
|
|
|
- <Timer className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
|
|
- <span>
|
|
|
- <strong>超时取消</strong>: 打印任务执行超过指定时间自动取消,避免任务阻塞
|
|
|
- </span>
|
|
|
- </li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- </TabsContent>
|
|
|
-
|
|
|
- {/* 模板配置 */}
|
|
|
- <TabsContent value={ConfigGroup.TEMPLATE} className="space-y-4">
|
|
|
- <Card>
|
|
|
- <CardHeader>
|
|
|
- <CardTitle>模板配置</CardTitle>
|
|
|
- <CardDescription>
|
|
|
- 配置小票和发货单的打印模板,支持变量替换
|
|
|
- </CardDescription>
|
|
|
- </CardHeader>
|
|
|
- <CardContent className="space-y-6">
|
|
|
- {configDefinitions
|
|
|
- .filter(def => def.group === ConfigGroup.TEMPLATE)
|
|
|
- .map(renderConfigItem)}
|
|
|
-
|
|
|
- {/* 模板变量说明 */}
|
|
|
- <div className="rounded-lg border bg-muted/50 p-4">
|
|
|
- <h4 className="font-medium mb-2">可用变量</h4>
|
|
|
- <div className="grid grid-cols-2 gap-4">
|
|
|
- <div>
|
|
|
- <h5 className="text-sm font-medium mb-1">订单信息</h5>
|
|
|
- <ul className="text-sm text-muted-foreground space-y-1">
|
|
|
- <li><code>{'{orderNo}'}</code> - 订单号</li>
|
|
|
- <li><code>{'{orderTime}'}</code> - 订单时间</li>
|
|
|
- <li><code>{'{totalAmount}'}</code> - 订单总金额</li>
|
|
|
- <li><code>{'{paymentMethod}'}</code> - 支付方式</li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
- <div>
|
|
|
- <h5 className="text-sm font-medium mb-1">收货信息</h5>
|
|
|
- <ul className="text-sm text-muted-foreground space-y-1">
|
|
|
- <li><code>{'{receiver}'}</code> - 收货人姓名</li>
|
|
|
- <li><code>{'{phone}'}</code> - 联系电话</li>
|
|
|
- <li><code>{'{address}'}</code> - 收货地址</li>
|
|
|
- <li><code>{'{shippingTime}'}</code> - 发货时间</li>
|
|
|
- </ul>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ ) : (
|
|
|
+ <>
|
|
|
+ {/* 基础配置 */}
|
|
|
+ {activeGroup === ConfigGroup.BASIC && (
|
|
|
+ <Card>
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle>基础配置</CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 配置飞鹅打印的基础功能,包括启用状态和默认打印机
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-6">
|
|
|
+ {configDefinitions
|
|
|
+ .filter(def => def.group === ConfigGroup.BASIC)
|
|
|
+ .map(renderConfigItem)}
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 打印策略 */}
|
|
|
+ {activeGroup === ConfigGroup.PRINT_POLICY && (
|
|
|
+ <Card>
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle>打印策略</CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 配置打印任务的触发条件、重试策略和超时设置
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-6">
|
|
|
+ {configDefinitions
|
|
|
+ .filter(def => def.group === ConfigGroup.PRINT_POLICY)
|
|
|
+ .map(renderConfigItem)}
|
|
|
+
|
|
|
+ {/* 策略说明 */}
|
|
|
+ <div className="rounded-lg border bg-muted/50 p-4">
|
|
|
+ <h4 className="font-medium mb-2">策略说明</h4>
|
|
|
+ <ul className="text-sm text-muted-foreground space-y-1">
|
|
|
+ <li className="flex items-start">
|
|
|
+ <Clock className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
|
|
+ <span>
|
|
|
+ <strong>防退款延迟</strong>: 支付成功后等待指定时间确认无退款再打印,避免无效打印
|
|
|
+ </span>
|
|
|
+ </li>
|
|
|
+ <li className="flex items-start">
|
|
|
+ <Repeat className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
|
|
+ <span>
|
|
|
+ <strong>重试机制</strong>: 打印失败时自动重试,最多重试指定次数,每次间隔指定时间
|
|
|
+ </span>
|
|
|
+ </li>
|
|
|
+ <li className="flex items-start">
|
|
|
+ <Timer className="h-4 w-4 mr-2 mt-0.5 flex-shrink-0" />
|
|
|
+ <span>
|
|
|
+ <strong>超时取消</strong>: 打印任务执行超过指定时间自动取消,避免任务阻塞
|
|
|
+ </span>
|
|
|
+ </li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ )}
|
|
|
+
|
|
|
+ {/* 模板配置 */}
|
|
|
+ {activeGroup === ConfigGroup.TEMPLATE && (
|
|
|
+ <Card>
|
|
|
+ <CardHeader>
|
|
|
+ <CardTitle>模板配置</CardTitle>
|
|
|
+ <CardDescription>
|
|
|
+ 配置小票和发货单的打印模板,支持变量替换
|
|
|
+ </CardDescription>
|
|
|
+ </CardHeader>
|
|
|
+ <CardContent className="space-y-6">
|
|
|
+ {configDefinitions
|
|
|
+ .filter(def => def.group === ConfigGroup.TEMPLATE)
|
|
|
+ .map(renderConfigItem)}
|
|
|
+
|
|
|
+ {/* 模板变量说明 */}
|
|
|
+ <div className="rounded-lg border bg-muted/50 p-4">
|
|
|
+ <h4 className="font-medium mb-2">可用变量</h4>
|
|
|
+ <div className="grid grid-cols-2 gap-4">
|
|
|
+ <div>
|
|
|
+ <h5 className="text-sm font-medium mb-1">订单信息</h5>
|
|
|
+ <ul className="text-sm text-muted-foreground space-y-1">
|
|
|
+ <li><code>{'{orderNo}'}</code> - 订单号</li>
|
|
|
+ <li><code>{'{orderTime}'}</code> - 订单时间</li>
|
|
|
+ <li><code>{'{totalAmount}'}</code> - 订单总金额</li>
|
|
|
+ <li><code>{'{paymentMethod}'}</code> - 支付方式</li>
|
|
|
+ </ul>
|
|
|
</div>
|
|
|
- <div className="mt-4">
|
|
|
- <h5 className="text-sm font-medium mb-1">商品信息</h5>
|
|
|
+ <div>
|
|
|
+ <h5 className="text-sm font-medium mb-1">收货信息</h5>
|
|
|
<ul className="text-sm text-muted-foreground space-y-1">
|
|
|
- <li><code>{'{goodsList}'}</code> - 商品列表(自动格式化)</li>
|
|
|
- <li><code>{'{goodsName}'}</code> - 商品名称</li>
|
|
|
- <li><code>{'{goodsPrice}'}</code> - 商品价格</li>
|
|
|
- <li><code>{'{goodsQuantity}'}</code> - 商品数量</li>
|
|
|
+ <li><code>{'{receiver}'}</code> - 收货人姓名</li>
|
|
|
+ <li><code>{'{phone}'}</code> - 联系电话</li>
|
|
|
+ <li><code>{'{address}'}</code> - 收货地址</li>
|
|
|
+ <li><code>{'{shippingTime}'}</code> - 发货时间</li>
|
|
|
</ul>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </CardContent>
|
|
|
- </Card>
|
|
|
- </TabsContent>
|
|
|
- </>
|
|
|
- )}
|
|
|
- </Tabs>
|
|
|
+ <div className="mt-4">
|
|
|
+ <h5 className="text-sm font-medium mb-1">商品信息</h5>
|
|
|
+ <ul className="text-sm text-muted-foreground space-y-1">
|
|
|
+ <li><code>{'{goodsList}'}</code> - 商品列表(自动格式化)</li>
|
|
|
+ <li><code>{'{goodsName}'}</code> - 商品名称</li>
|
|
|
+ <li><code>{'{goodsPrice}'}</code> - 商品价格</li>
|
|
|
+ <li><code>{'{goodsQuantity}'}</code> - 商品数量</li>
|
|
|
+ </ul>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </CardContent>
|
|
|
+ </Card>
|
|
|
+ )}
|
|
|
+ </>
|
|
|
+ )}
|
|
|
|
|
|
{/* 配置列表表格视图(备用) */}
|
|
|
<Card>
|
|
|
@@ -815,7 +855,16 @@ export const PrintConfigManagement: React.FC<PrintConfigManagementProps> = ({
|
|
|
<Button
|
|
|
variant="ghost"
|
|
|
size="sm"
|
|
|
- onClick={() => config && openEditDialog(config)}
|
|
|
+ onClick={() => {
|
|
|
+ // 如果config不存在,创建一个临时的配置对象
|
|
|
+ const configToEdit = config || {
|
|
|
+ configKey: definition.key,
|
|
|
+ configValue: definition.defaultValue,
|
|
|
+ configType: definition.type,
|
|
|
+ description: definition.description
|
|
|
+ };
|
|
|
+ openEditDialog(configToEdit);
|
|
|
+ }}
|
|
|
title="编辑"
|
|
|
>
|
|
|
编辑
|