To create the StarComponent based on the provided plan, here's a breakdown of each section with implementation guidance:
isReadOnly: boolean - Determines if the component is in read-only mode.hideScore: boolean - Optionally hides the score display.score: number - The current score to be displayed.onClick: (value: number) => void - Callback for when a star is clicked.onSubmit: (value: number) => void - Callback for submitting the score.renderStar: (value: number) => HTMLElement - Custom rendering function for stars.maxStar: number - The maximum number of stars (default is 5).score: number - Represents the selected score.onChange: (value: number) => void - Handler for updating the score.value and max.onChange and onSubmit callbacks to handle interactions.aria-live attributes for screen reader support.rem and em units for scalable sizing and to support different screen resolutions.Here's a basic example of how the structure of the StarComponent might look:
import React, { useState } from 'react';
const StarComponent = ({
isReadOnly = false,
hideScore = false,
score = 0,
maxStar = 5,
onClick,
onSubmit,
renderStar,
}) => {
const [currentScore, setCurrentScore] = useState(score);
const handleClick = (value) => {
if (!isReadOnly) {
setCurrentScore(value);
if (onClick) onClick(value);
}
};
const handleSubmit = () => {
if (onSubmit) onSubmit(currentScore);
};
return (
<div className="star-component">
{!hideScore && <span className="score-label">{currentScore}</span>}
<div className="stars">
{[...Array(maxStar)].map((_, index) => {
const value = index + 1;
return (
<div
key={value}
className={`star ${value <= currentScore ? 'filled' : ''}`}
onClick={() => handleClick(value)}
style={{ width: value <= currentScore ? '100%' : `${(currentScore % 1) * 100}%` }}
>
{renderStar ? renderStar(value) : '★'}
</div>
);
})}
</div>
{!isReadOnly && (
<button onClick={handleSubmit}>Submit</button>
)}
</div>
);
};
export default StarComponent;
Here's some CSS that aligns with the optimization guidelines:
.star-component {
display: inline-flex;
align-items: center;
}
.score-label {
margin-right: 8px;
font-size: 1rem;
}
.stars {
display: flex;
}
.star {
cursor: pointer;
font-size: 2rem;
overflow: hidden;
}
.star.filled {
color: gold;
}
This structure covers the key requirements, including customizable props, star rendering logic, score display, and callbacks for interaction. Make sure to refine CSS and JavaScript further based on specific project requirements and accessibility standards.
如果需要在 StarComponent 中发出请求并获取数据,可以在组件挂载时使用 useEffect 钩子来请求数据(例如获取初始评分),然后在提交评分时再发出提交请求。以下是一个完整的示例,展示如何在组件挂载时获取数据,并在用户提交评分时发送请求。
StarComponent 中发出请求和获取数据假设我们要获取和提交评分数据到 /api/getRating 和 /api/submitRating 端点。
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const StarComponent = ({
isReadOnly = false,
hideScore = false,
initialScore = 0,
maxStar = 5,
onClick,
onSubmit,
renderStar,
}) => {
const [currentScore, setCurrentScore] = useState(initialScore);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [fetchLoading, setFetchLoading] = useState(true);
// 获取初始评分数据
useEffect(() => {
const fetchInitialScore = async () => {
try {
setFetchLoading(true);
const response = await axios.get('/api/getRating');
setCurrentScore(response.data.score); // 假设服务器返回的数据包含 `score` 字段
} catch (error) {
console.error('获取评分数据失败:', error);
setError('无法加载评分数据');
} finally {
setFetchLoading(false);
}
};
fetchInitialScore();
}, []);
const handleClick = (value) => {
if (!isReadOnly) {
setCurrentScore(value);
if (onClick) onClick(value);
}
};
const handleSubmit = async () => {
if (onSubmit) onSubmit(currentScore);
setLoading(true);
setError(null);
try {
// 提交评分数据
const response = await axios.post('/api/submitRating', { score: currentScore });
console.log('评分提交成功:', response.data);
// 可以在这里添加成功处理逻辑,例如显示成功消息
} catch (error) {
console.error('评分提交失败:', error);
setError('提交评分失败,请重试');
} finally {
setLoading(false);
}
};
if (fetchLoading) {
return <p>加载中...</p>; // 显示加载状态
}
return (
<div className="star-component">
{!hideScore && <span className="score-label">{currentScore}</span>}
<div className="stars">
{[...Array(maxStar)].map((_, index) => {
const value = index + 1;
return (
<div
key={value}
className={`star ${value <= currentScore ? 'filled' : ''}`}
onClick={() => handleClick(value)}
style={{ width: value <= currentScore ? '100%' : `${(currentScore % 1) * 100)%` }}
>
{renderStar ? renderStar(value) : '★'}
</div>
);
})}
</div>
{!isReadOnly && (
<button onClick={handleSubmit} disabled={loading}>
{loading ? '提交中...' : '提交'}
</button>
)}
{error && <p className="error-message">{error}</p>}
</div>
);
};
export default StarComponent;
获取初始评分数据:
useEffect 中),我们调用 fetchInitialScore 函数发送一个 GET 请求到 /api/getRating。currentScore 为服务器返回的评分数据。如果请求失败,则设置 error 状态以显示错误消息。fetchLoading 用来控制加载状态,确保在数据加载完成之前显示"加载中..."消息。评分点击处理:
handleClick 函数用于在用户点击星星时更新评分,如果组件不是只读模式,则更新评分并触发 onClick 回调(如果提供)。提交评分数据:
handleSubmit 函数用于在用户点击"提交"按钮时发送 POST 请求到 /api/submitRating,提交当前评分数据。加载状态:
fetchLoading 为 true 时,显示"加载中..."文本,直到初始数据加载完成。错误处理:
/api/getRating 和 /api/submitRating 端点在后端已配置并能处理相应的请求。