表格支持
This commit is contained in:
@@ -24,6 +24,13 @@ import { FieldSelect } from './PsSelect';
|
||||
import { CommitInput } from './CommitInput';
|
||||
import { useAppDialog } from './AppDialog';
|
||||
import { useIsNarrowScreen } from '../hooks/useIsMobile';
|
||||
import {
|
||||
TablePropertyFields,
|
||||
TableCellContentFields,
|
||||
scaleTableElementSize,
|
||||
} from './TablePropertyFields';
|
||||
import type { TableCellCoord } from '../tableUtils';
|
||||
import { expandTableForPadding } from '../tableUtils';
|
||||
import {
|
||||
Sliders,
|
||||
LayoutGrid,
|
||||
@@ -63,6 +70,7 @@ import {
|
||||
QrCode,
|
||||
Image as ImageIcon,
|
||||
Upload,
|
||||
Table2,
|
||||
} from 'lucide-react';
|
||||
|
||||
const PANEL_COLLAPSE_STORAGE_KEY = 'label_designer_panel_collapse_v2';
|
||||
@@ -166,6 +174,8 @@ interface PropSidebarProps {
|
||||
onChangeTab: (tab: 'element' | 'paper') => void;
|
||||
paper: PaperConfig;
|
||||
onSelectElements?: (ids: string[]) => void;
|
||||
selectedTableCells?: TableCellCoord[];
|
||||
onSelectTableCells?: (cells: TableCellCoord[]) => void;
|
||||
variant?: 'desktop' | 'mobile';
|
||||
mobileModule?: MobilePropModule;
|
||||
onMobileModuleChange?: (module: MobilePropModule) => void;
|
||||
@@ -186,6 +196,8 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
onChangeTab,
|
||||
paper,
|
||||
onSelectElements,
|
||||
selectedTableCells = [],
|
||||
onSelectTableCells,
|
||||
variant = 'desktop',
|
||||
mobileModule: mobileModuleProp,
|
||||
onMobileModuleChange,
|
||||
@@ -266,6 +278,14 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
});
|
||||
};
|
||||
|
||||
const commitTableAwareChange = (updatedElem: TemplateElement) => {
|
||||
if (updatedElem.type === 'table') {
|
||||
handleElemChange(expandTableForPadding(updatedElem));
|
||||
} else {
|
||||
handleElemChange(updatedElem);
|
||||
}
|
||||
};
|
||||
|
||||
const nudgeableElementIds = useMemo(() => {
|
||||
const idSet = new Set(selectedElementIds);
|
||||
return template.elements
|
||||
@@ -639,43 +659,64 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
</label>
|
||||
</div>
|
||||
)}
|
||||
<PropTextarea
|
||||
value={selectedElem.content}
|
||||
onCommit={(val) => handleElemChange({ ...selectedElem, content: val })}
|
||||
rows={selectedElem.type === 'image' ? 3 : 2}
|
||||
className="ps-field resize-none"
|
||||
placeholder={
|
||||
selectedElem.type === 'image'
|
||||
? '图片 URL 或 data URI,也可使用 {变量名}'
|
||||
: '输入文本,使用 {变量名} 引用变量'
|
||||
}
|
||||
/>
|
||||
{templateVariables.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<div className="text-[9px] text-[#777] font-medium">插入变量到内容</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{templateVariables.map((v) => (
|
||||
<button
|
||||
key={v}
|
||||
type="button"
|
||||
onClick={() => insertVariable(v)}
|
||||
className="ps-btn text-[9px] font-mono"
|
||||
>
|
||||
{`{${v}}`}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{selectedElem.type !== 'table' && (
|
||||
<>
|
||||
<PropTextarea
|
||||
value={selectedElem.content}
|
||||
onCommit={(val) => handleElemChange({ ...selectedElem, content: val })}
|
||||
rows={selectedElem.type === 'image' ? 3 : 2}
|
||||
className="ps-field resize-none"
|
||||
placeholder={
|
||||
selectedElem.type === 'image'
|
||||
? '图片 URL 或 data URI,也可使用 {变量名}'
|
||||
: '输入文本,使用 {变量名} 引用变量'
|
||||
}
|
||||
/>
|
||||
{templateVariables.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<div className="text-[9px] text-[#777] font-medium">插入变量到内容</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{templateVariables.map((v) => (
|
||||
<button
|
||||
key={v}
|
||||
type="button"
|
||||
onClick={() => insertVariable(v)}
|
||||
className="ps-btn text-[9px] font-mono"
|
||||
>
|
||||
{`{${v}}`}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{selectedElem.type === 'table' && (
|
||||
<p className="text-[10px] text-[#777] leading-normal">
|
||||
表格各单元格内容独立设置,默认为空。点击画布上的单元格后在属性面板编辑。
|
||||
</p>
|
||||
)}
|
||||
{(selectedElem.type === 'text' ||
|
||||
selectedElem.type === 'barcode' ||
|
||||
selectedElem.type === 'qrcode' ||
|
||||
selectedElem.type === 'image') && (
|
||||
selectedElem.type === 'image' ||
|
||||
selectedElem.type === 'table') && (
|
||||
<div className="pt-2 border-t border-[#3a3a3a]">
|
||||
{selectedElem.type === 'table' ? (
|
||||
<TableCellContentFields
|
||||
elem={selectedElem}
|
||||
selectedCells={selectedTableCells}
|
||||
onChange={handleElemChange}
|
||||
templateVariables={templateVariables}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<ValueExtractFields elem={selectedElem} onChange={handleElemChange} />
|
||||
{selectedElem.type === 'text' && (
|
||||
<TextDisplayAffixFields elem={selectedElem} onChange={handleElemChange} />
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{renderVariableManagementBlock()}
|
||||
@@ -954,6 +995,15 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
</div>
|
||||
|
||||
{/* Text Settings */}
|
||||
{selectedElem.type === 'table' && selectedElementIds.length === 1 && (
|
||||
<TablePropertyFields
|
||||
elem={selectedElem}
|
||||
selectedCells={selectedTableCells}
|
||||
onChange={handleElemChange}
|
||||
onSelectTableCells={onSelectTableCells}
|
||||
templateVariables={templateVariables}
|
||||
/>
|
||||
)}
|
||||
{selectedElem.type === 'text' && (
|
||||
<div className="space-y-3 ps-section-divider">
|
||||
<h5 className="ps-section-title">
|
||||
@@ -1406,9 +1456,11 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
value={selectedElem.width}
|
||||
onCommit={(val) => {
|
||||
const wVal = Math.max(2, Number(val) || 2);
|
||||
const updated = { ...selectedElem, width: wVal };
|
||||
let updated = { ...selectedElem, width: wVal };
|
||||
if (selectedElem.type === 'qrcode') {
|
||||
updated.height = wVal;
|
||||
} else if (selectedElem.type === 'table') {
|
||||
updated = scaleTableElementSize(updated, wVal, updated.height);
|
||||
}
|
||||
handleElemChange(updated);
|
||||
}}
|
||||
@@ -1425,7 +1477,11 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
disabled={selectedElem.type === 'qrcode'}
|
||||
onCommit={(val) => {
|
||||
const hVal = Math.max(2, Number(val) || 2);
|
||||
handleElemChange({ ...selectedElem, height: hVal });
|
||||
if (selectedElem.type === 'table') {
|
||||
handleElemChange(scaleTableElementSize(selectedElem, selectedElem.width, hVal));
|
||||
} else {
|
||||
handleElemChange({ ...selectedElem, height: hVal });
|
||||
}
|
||||
}}
|
||||
className={`ps-field font-mono ${selectedElem.type === 'qrcode' ? 'opacity-50 select-none' : ''
|
||||
}`}
|
||||
@@ -1508,7 +1564,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
if (v === 'transparent' || v === '') {
|
||||
handleElemChange({
|
||||
...selectedElem,
|
||||
backgroundColor: 'transparent',
|
||||
backgroundColor: '#ffffff',
|
||||
backgroundOpacity: 0,
|
||||
});
|
||||
} else {
|
||||
@@ -1573,7 +1629,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
step="0.5"
|
||||
value={selectedElem.padding ?? 0}
|
||||
onCommit={(val) =>
|
||||
handleElemChange({
|
||||
commitTableAwareChange({
|
||||
...selectedElem,
|
||||
padding: Math.max(0, Math.min(10, Number(val) || 0)),
|
||||
})
|
||||
@@ -1606,7 +1662,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
} else {
|
||||
updated[key] = Math.max(0, Math.min(10, Number(val) || 0));
|
||||
}
|
||||
handleElemChange(updated);
|
||||
commitTableAwareChange(updated);
|
||||
}}
|
||||
className="ps-field font-mono text-[10px] flex-1 min-w-0"
|
||||
placeholder="—"
|
||||
@@ -1863,6 +1919,7 @@ export const PropSidebar: React.FC<PropSidebarProps> = ({
|
||||
{elem.type === 'barcode' && <Barcode className="w-3 h-3 text-[#666]" />}
|
||||
{elem.type === 'qrcode' && <QrCode className="w-3 h-3 text-[#666]" />}
|
||||
{elem.type === 'image' && <ImageIcon className="w-3 h-3 text-[#666]" />}
|
||||
{elem.type === 'table' && <Table2 className="w-3 h-3 text-[#666]" />}
|
||||
</span>
|
||||
<span className="truncate" title={elem.name}>
|
||||
{elem.name}
|
||||
|
||||
Reference in New Issue
Block a user