diff --git a/App.jsx b/App.jsx
index 34a005d..910942f 100644
--- a/App.jsx
+++ b/App.jsx
@@ -411,7 +411,10 @@ function gatherExportTerms(p, categories) {
else terms.add(r.labelKey);
});
if (hasPortRow) terms.add('输出');
- if (p.notes && p.notes.trim()) { terms.add('备注'); terms.add(p.notes.trim()); }
+ // 对客卖点/主要特点:每行一条,都是要给客户看的文字,需要翻译
+ (p.sellingPoints || '').split('\n').map(l => l.trim()).filter(Boolean).forEach(line => terms.add(line));
+ // 注意:备注(notes)字段故意不收集、不导出——这里经常被用来记内部信息(成本价、
+ // 采购渠道、库存数量这类),一旦跟着PDF发给客户后果很麻烦,所以规格书导出永远不碰这个字段。
return Array.from(terms);
}
@@ -430,6 +433,8 @@ const blankProduct = () => ({
batterySpec: '', // 迷你UPS专属:电池数量和容量,如 "5000mAh*4"
dimensions: '', // 迷你UPS专属:尺寸(mm),如 "210*130*40"
netWeight: '', // 迷你UPS专属:净重(kg)
+ productImageFileName: '', productImageOriginalName: '', // 产品主图,导出多语言PDF时展示
+ sellingPoints: '', // 对客卖点/主要特点,每行一条,专门给客户看的(和下面的costPrice/notes内部信息彻底分开,不会一起导出)
costPrice: '', notes: '',
});
@@ -1112,6 +1117,14 @@ const Styles = () => (
.spec-sheet-table { margin-top: 0; border: 1px solid var(--copper-soft); border-top: none; }
.spec-sheet-table td.spec-label { background: #F7F1E8; font-weight: 600; width: 38%; color: #6b5a3e; }
.spec-sheet-table tr:nth-child(even) td { background: #FBF8F3; }
+ .spec-sheet-two-col { display: flex; gap: 32px; align-items: flex-start; }
+ .spec-sheet-left { flex: 0 0 240px; }
+ .spec-sheet-right { flex: 1; min-width: 0; }
+ .spec-sheet-image { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 6px; border: 1px solid var(--copper-soft); }
+ .spec-sheet-points { margin: 14px 0 0; padding-left: 18px; font-size: 12.5px; color: #444; line-height: 1.7; }
+ .spec-sheet-points li { margin-bottom: 4px; }
+ [dir="rtl"] .spec-sheet-two-col { flex-direction: row-reverse; }
+ [dir="rtl"] .spec-sheet-points { padding-left: 0; padding-right: 18px; }
@media print {
@@ -2506,7 +2519,7 @@ function MainApp({ username, onLogout }) {
{productDraft ? (
-
setTaxManageMode(mode)} />
+ setTaxManageMode(mode)} onNotify={notify} />
) : selectedProduct ? (
startEditProduct(selectedProduct)} onDelete={() => deleteProduct(selectedProduct.id)}
@@ -3442,7 +3455,7 @@ function CustomerDetail({
/* ----------------------------- product form / detail ----------------------------- */
-function ProductForm({ draft, setDraft, onSave, onCancel, categories, onManage }) {
+function ProductForm({ draft, setDraft, onSave, onCancel, categories, onManage, onNotify }) {
const set = (patch) => setDraft(d => ({ ...d, ...patch }));
const cat = (categories || []).find(c => c.id === draft.category);
const brandOptions = cat ? cat.brands : [];
@@ -3451,6 +3464,24 @@ function ProductForm({ draft, setDraft, onSave, onCancel, categories, onManage }
const isMiniUps = isUps && draft.type === '迷你UPS';
const maxKW = computeMaxPowerKW(draft.ratedPower, draft.ratedPowerUnit, draft.powerFactor);
const extraNames = draft.extraNames || [];
+ const [imageUploading, setImageUploading] = useState(false);
+ async function handleImagePick(file) {
+ if (!file) return;
+ setImageUploading(true);
+ try {
+ const res = await uploadFile(draft.id, file);
+ set({ productImageFileName: res.fileName, productImageOriginalName: res.originalName });
+ } catch (e) {
+ if (onNotify) onNotify('图片上传失败,请重试');
+ } finally {
+ setImageUploading(false);
+ }
+ }
+ async function handleImageRemove() {
+ if (!draft.productImageFileName) return;
+ try { await deleteFile(draft.id, draft.productImageFileName); } catch (e) {}
+ set({ productImageFileName: '', productImageOriginalName: '' });
+ }
function addExtraName() { set({ extraNames: [...extraNames, ''] }); }
function updateExtraName(idx, val) { const arr = [...extraNames]; arr[idx] = val; set({ extraNames: arr }); }
function removeExtraName(idx) { set({ extraNames: extraNames.filter((_, i) => i !== idx) }); }
@@ -3646,8 +3677,45 @@ function ProductForm({ draft, setDraft, onSave, onCancel, categories, onManage }
)}
+
+
+ 产品主图(可选,用于多语言PDF导出)
+
+ {draft.productImageFileName ? (
+
+

+
{draft.productImageOriginalName}
+
+
+
+ ) : (
+
+ )}
+
+
+
+ 对客卖点 / 主要特点(可选,每行一条,会显示在多语言PDF导出的资料里)
+
+
正本价格(元) set({ costPrice: e.target.value })} placeholder="采购/成本价,人民币" />
- 备注 / 卖点说明
+
+ 内部备注(仅自己可见,不会出现在任何导出的文档里)
+
@@ -3701,28 +3769,65 @@ function ProductDetail({ product: p, onEdit, onDelete, categories, exportLang, o
-
-
{p.name || '—'}
- {p.brand &&
{p.brand}
}
-
-
{L('产品规格书')}
-
-
- | {L('型号')} | {p.name || '—'} |
- {p.brand && | {L('品牌')} | {p.brand} |
}
- | {L('产品大类')} | {cat ? L(cat.name) : '—'} |
- {p.type && | {L('细分品类')} | {L(p.type)} |
}
- {showMachineVariant && | {L('机型')} | {L(p.machineVariant)} |
}
- {exportRows.map((r, i) => (
-
- | {r.labelKey === '__port__' ? `${r.portType} ${L('输出')}` : L(r.labelKey)} |
- {r.value} |
-
- ))}
- {p.notes && p.notes.trim() && | {L('备注')} | {L(p.notes.trim())} |
}
-
-
-
{L('* 产品规格如有变动,恕不另行通知')}
+ {(() => {
+ const sellingPointLines = (p.sellingPoints || '').split('\n').map(l => l.trim()).filter(Boolean);
+ const specTable = (
+
+
+ | {L('型号')} | {p.name || '—'} |
+ {p.brand && | {L('品牌')} | {p.brand} |
}
+ | {L('产品大类')} | {cat ? L(cat.name) : '—'} |
+ {p.type && | {L('细分品类')} | {L(p.type)} |
}
+ {showMachineVariant && | {L('机型')} | {L(p.machineVariant)} |
}
+ {exportRows.map((r, i) => (
+
+ | {r.labelKey === '__port__' ? `${r.portType} ${L('输出')}` : L(r.labelKey)} |
+ {r.value} |
+
+ ))}
+
+
+ );
+ const disclaimer =
{L('* 产品规格如有变动,恕不另行通知')}
;
+
+ if (p.productImageFileName) {
+ return (
+
+
+

+
{p.name || '—'}
+ {p.brand &&
{p.brand}
}
+ {sellingPointLines.length > 0 && (
+
+ {sellingPointLines.map((line, i) => - {L(line)}
)}
+
+ )}
+
+
+
{L('产品规格书')}
+ {specTable}
+ {disclaimer}
+
+
+ );
+ }
+ return (
+ <>
+
+
{p.name || '—'}
+ {p.brand &&
{p.brand}
}
+
+ {sellingPointLines.length > 0 && (
+
+ {sellingPointLines.map((line, i) => - {L(line)}
)}
+
+ )}
+
{L('产品规格书')}
+ {specTable}
+ {disclaimer}
+ >
+ );
+ })()}