| | |
| | | Grid, |
| | | Chip, |
| | | Button, |
| | | Divider, |
| | | Skeleton, |
| | | useTheme, |
| | | } from '@mui/material'; |
| | |
| | | const ruleList = useMemo(() => normalizeDirRule(info?.dirRule), [info?.dirRule]); |
| | | |
| | | const spatialItems = [ |
| | | { label: translate('table.field.code.x'), value: formatNumber(info?.x, 0), hideWhenEmpty: true }, |
| | | { label: translate('table.field.code.y'), value: formatNumber(info?.y, 0), hideWhenEmpty: true }, |
| | | { |
| | | label: translate('page.map.insight.code.fields.mapPosition', { _: '地图坐标' }), |
| | | render: () => ( |
| | |
| | | /> |
| | | )} |
| | | <FieldGrid items={spatialItems} loading={loading} /> |
| | | <Button |
| | | variant="contained" |
| | | color="primary" |
| | | startIcon={<OpenInNewIcon />} |
| | | onClick={handleOpenDetail} |
| | | disabled={!info?.id} |
| | | sx={{ alignSelf: 'flex-start', textTransform: 'none', px: 3 }} |
| | | > |
| | | {translate('page.map.insight.code.actions.openDetail', { _: '编辑' })} |
| | | </Button> |
| | | </Stack> |
| | | </Paper> |
| | | <Button |
| | | variant="contained" |
| | | color="primary" |
| | | startIcon={<OpenInNewIcon />} |
| | | onClick={handleOpenDetail} |
| | | disabled={!info?.id} |
| | | sx={{ alignSelf: 'flex-start', textTransform: 'none', px: 3 }} |
| | | > |
| | | {translate('page.map.insight.code.actions.openDetail', { _: '编辑' })} |
| | | </Button> |
| | | </Stack> |
| | | </Grid> |
| | | <Grid item xs={12} md={7}> |
| | |
| | | /> |
| | | </InfoPanel> |
| | | <InfoPanel title={translate('page.map.insight.code.relations.routes', { _: '关联路线' })}> |
| | | <RelationsChips |
| | | <RelationsList |
| | | items={routeRelations} |
| | | emptyLabel={translate('page.map.insight.code.relations.empty', { _: '暂无关联信息' })} |
| | | /> |
| | |
| | | label={`+${items.length - MAX_RELATION_ITEMS}`} |
| | | size="small" |
| | | /> |
| | | )} |
| | | </Stack> |
| | | ); |
| | | }; |
| | | |
| | | const RelationsList = ({ items, emptyLabel }) => { |
| | | if (!items?.length) { |
| | | return ( |
| | | <Typography variant="body2" color="text.disabled"> |
| | | {emptyLabel} |
| | | </Typography> |
| | | ); |
| | | } |
| | | |
| | | return ( |
| | | <Stack spacing={1}> |
| | | {items.slice(0, MAX_RELATION_ITEMS).map((item, index) => ( |
| | | <Box |
| | | key={`${getRelationKey(item)}-${index}`} |
| | | sx={{ |
| | | px: 1.25, |
| | | py: 0.75, |
| | | borderRadius: 999, |
| | | border: '1px solid', |
| | | borderColor: 'divider', |
| | | backgroundColor: 'background.default', |
| | | }} |
| | | > |
| | | <Typography variant="body2" sx={{ lineHeight: 1.2 }}> |
| | | {getRelationLabel(item)} |
| | | </Typography> |
| | | </Box> |
| | | ))} |
| | | {items.length > MAX_RELATION_ITEMS && ( |
| | | <Typography variant="caption" color="text.secondary"> |
| | | +{items.length - MAX_RELATION_ITEMS} |
| | | </Typography> |
| | | )} |
| | | </Stack> |
| | | ); |
| | |
| | | } |
| | | /> |
| | | </Box> |
| | | <Divider /> |
| | | {loading ? ( |
| | | <Skeleton variant="rounded" height={220} /> |
| | | <Skeleton variant="rounded" height={188} /> |
| | | ) : ( |
| | | <DirectionRuleCompass rules={rules} translate={translate} /> |
| | | )} |
| | |
| | | const DirectionRuleCompass = ({ rules, translate }) => { |
| | | const theme = useTheme(); |
| | | const enabledCount = rules.filter(rule => rule.enabled).length; |
| | | const placement = { |
| | | 0: { gridColumn: 2, gridRow: 1 }, |
| | | 90: { gridColumn: 3, gridRow: 2 }, |
| | | 180: { gridColumn: 2, gridRow: 3 }, |
| | | 270: { gridColumn: 1, gridRow: 2 }, |
| | | }; |
| | | const topRule = rules.find(rule => rule.angle === 0); |
| | | const rightRule = rules.find(rule => rule.angle === 90); |
| | | const bottomRule = rules.find(rule => rule.angle === 180); |
| | | const leftRule = rules.find(rule => rule.angle === 270); |
| | | |
| | | return ( |
| | | <Stack spacing={0.85} alignItems="center"> |
| | | <DirectionRuleCard rule={topRule} translate={translate} /> |
| | | <Stack direction="row" spacing={0.6} alignItems="center" justifyContent="center"> |
| | | <DirectionRuleCard rule={leftRule} translate={translate} /> |
| | | <Box |
| | | sx={{ |
| | | width: 52, |
| | | height: 42, |
| | | borderRadius: 2.5, |
| | | border: '1px dashed', |
| | | borderColor: 'divider', |
| | | backgroundColor: alpha(theme.palette.primary.main, 0.04), |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | justifyContent: 'center', |
| | | textAlign: 'center', |
| | | flexShrink: 0, |
| | | }} |
| | | > |
| | | <Typography variant="h5" sx={{ lineHeight: 1, fontWeight: 700, fontSize: '0.98rem' }}> |
| | | {enabledCount}/{rules.length} |
| | | </Typography> |
| | | </Box> |
| | | <DirectionRuleCard rule={rightRule} translate={translate} /> |
| | | </Stack> |
| | | <DirectionRuleCard rule={bottomRule} translate={translate} /> |
| | | </Stack> |
| | | ); |
| | | }; |
| | | |
| | | const DirectionRuleCard = ({ rule, translate }) => { |
| | | const theme = useTheme(); |
| | | |
| | | if (!rule) { |
| | | return null; |
| | | } |
| | | |
| | | const statusText = translate( |
| | | rule.enabled ? 'page.code.dirRule.status.enabled' : 'page.code.dirRule.status.disabled' |
| | | ); |
| | | |
| | | return ( |
| | | <Box |
| | | sx={{ |
| | | display: 'grid', |
| | | gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', |
| | | gridTemplateRows: 'repeat(3, minmax(58px, auto))', |
| | | gap: 1, |
| | | alignItems: 'stretch', |
| | | width: 68, |
| | | height: 42, |
| | | borderRadius: 2.5, |
| | | border: '1px solid', |
| | | borderColor: rule.enabled ? 'success.light' : 'error.light', |
| | | backgroundColor: rule.enabled |
| | | ? alpha(theme.palette.success.main, 0.08) |
| | | : alpha(theme.palette.error.main, 0.08), |
| | | px: 0.5, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | justifyContent: 'center', |
| | | textAlign: 'center', |
| | | flexShrink: 0, |
| | | }} |
| | | > |
| | | <Box |
| | | sx={{ |
| | | gridColumn: 2, |
| | | gridRow: 2, |
| | | borderRadius: 3, |
| | | border: '1px dashed', |
| | | borderColor: 'divider', |
| | | backgroundColor: alpha(theme.palette.primary.main, 0.04), |
| | | px: 1, |
| | | py: 0.75, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | justifyContent: 'center', |
| | | textAlign: 'center', |
| | | }} |
| | | > |
| | | <Typography variant="h6" sx={{ lineHeight: 1.1, fontWeight: 700 }}> |
| | | {enabledCount}/{rules.length} |
| | | <Stack spacing={0.1}> |
| | | <Typography variant="subtitle2" sx={{ fontWeight: 700, lineHeight: 1, fontSize: '0.88rem' }}> |
| | | {rule.angle}° |
| | | </Typography> |
| | | </Box> |
| | | {rules.map(rule => { |
| | | const position = placement[rule.angle] || { gridColumn: 'auto', gridRow: 'auto' }; |
| | | const statusText = translate( |
| | | rule.enabled ? 'page.code.dirRule.status.enabled' : 'page.code.dirRule.status.disabled' |
| | | ); |
| | | |
| | | return ( |
| | | <Box |
| | | key={rule.angle} |
| | | sx={{ |
| | | ...position, |
| | | borderRadius: 3, |
| | | border: '1px solid', |
| | | borderColor: rule.enabled ? 'success.light' : 'error.light', |
| | | backgroundColor: rule.enabled |
| | | ? alpha(theme.palette.success.main, 0.08) |
| | | : alpha(theme.palette.error.main, 0.08), |
| | | px: 1, |
| | | py: 0.75, |
| | | minHeight: 60, |
| | | display: 'flex', |
| | | alignItems: 'center', |
| | | justifyContent: 'center', |
| | | textAlign: 'center', |
| | | }} |
| | | > |
| | | <Stack spacing={0.35}> |
| | | <Typography variant="subtitle2" sx={{ fontWeight: 700, lineHeight: 1 }}> |
| | | {rule.angle}° |
| | | </Typography> |
| | | <Typography |
| | | variant="caption" |
| | | sx={{ |
| | | lineHeight: 1.1, |
| | | color: rule.enabled ? 'success.dark' : 'error.dark', |
| | | fontWeight: 600, |
| | | }} |
| | | > |
| | | {statusText} |
| | | </Typography> |
| | | </Stack> |
| | | </Box> |
| | | ); |
| | | })} |
| | | <Typography |
| | | variant="caption" |
| | | sx={{ |
| | | lineHeight: 1.05, |
| | | fontSize: '0.64rem', |
| | | color: rule.enabled ? 'success.dark' : 'error.dark', |
| | | fontWeight: 600, |
| | | }} |
| | | > |
| | | {statusText} |
| | | </Typography> |
| | | </Stack> |
| | | </Box> |
| | | ); |
| | | }; |