This commit is contained in:
susie-laptop 2025-04-11 16:56:20 -04:00
parent 676e216fe2
commit e4a2d76644
9 changed files with 97 additions and 63 deletions

View File

@ -4,6 +4,7 @@ import SvgIcon from './SvgIcon';
// //
const departmentGroups = { const departmentGroups = {
技术部: ['开发组', '测试组', '运维组', '架构组', '安全组'], 技术部: ['开发组', '测试组', '运维组', '架构组', '安全组'],
测试部: ['测试组'],
产品部: ['产品规划组', '用户研究组', '交互设计组', '项目管理组'], 产品部: ['产品规划组', '用户研究组', '交互设计组', '项目管理组'],
市场部: ['品牌推广组', '市场调研组', '客户关系组', '社交媒体组'], 市场部: ['品牌推广组', '市场调研组', '客户关系组', '社交媒体组'],
行政部: ['人事组', '财务组', '行政管理组', '后勤组'], 行政部: ['人事组', '财务组', '行政管理组', '后勤组'],

View File

@ -54,12 +54,12 @@ const SearchBar = ({
}; };
}, []); }, []);
// //
useEffect(() => { useEffect(() => {
if (isSearching && searchResults.length > 0) { if (isSearching) {
setShowDropdown(true); setShowDropdown(true);
} }
}, [isSearching, searchResults]); }, [isSearching]);
// //
const handleInputChange = (e) => { const handleInputChange = (e) => {
@ -111,8 +111,8 @@ const SearchBar = ({
</div> </div>
</form> </form>
{/* 搜索结果下拉框 - 仅在用户搜索且有结果时显示 */} {/* 搜索结果下拉框 - 在用户搜索后显示,无论是否有结果 */}
{showDropdown && (isSearchLoading || searchResults?.length > 0) && ( {showDropdown && (
<div <div
className={`position-absolute bg-white shadow-sm mt-1 w-100 search-results-dropdown z-1 ${ className={`position-absolute bg-white shadow-sm mt-1 w-100 search-results-dropdown z-1 ${
cornerStyle === 'rounded' ? 'rounded-3' : '' cornerStyle === 'rounded' ? 'rounded-3' : ''

View File

@ -16,6 +16,7 @@ import DeleteConfirmModal from './components/DeleteConfirmModal';
// //
const departmentGroups = { const departmentGroups = {
技术部: ['开发组', '测试组', '运维组', '架构组', '安全组'], 技术部: ['开发组', '测试组', '运维组', '架构组', '安全组'],
测试部: ['测试组'],
产品部: ['产品规划组', '用户研究组', '交互设计组', '项目管理组'], 产品部: ['产品规划组', '用户研究组', '交互设计组', '项目管理组'],
市场部: ['品牌推广组', '市场调研组', '客户关系组', '社交媒体组'], 市场部: ['品牌推广组', '市场调研组', '客户关系组', '社交媒体组'],
行政部: ['人事组', '财务组', '行政管理组', '后勤组'], 行政部: ['人事组', '财务组', '行政管理组', '后勤组'],
@ -46,7 +47,6 @@ export default function SettingsTab({ knowledgeBase }) {
const [isSubmitting, setIsSubmitting] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false);
const [showDeleteConfirm, setShowDeleteConfirm] = useState(false); const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
const [availableGroups, setAvailableGroups] = useState([]); const [availableGroups, setAvailableGroups] = useState([]);
const [showUploadModal, setShowUploadModal] = useState(false);
// //
useEffect(() => { useEffect(() => {

View File

@ -10,6 +10,7 @@ import DocumentPreviewModal from './DocumentPreviewModal';
const DocumentList = ({ knowledgeBaseId }) => { const DocumentList = ({ knowledgeBaseId }) => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const { items, loading, error } = useSelector((state) => state.knowledgeBase.documents); const { items, loading, error } = useSelector((state) => state.knowledgeBase.documents);
const currentKnowledgeBase = useSelector((state) => state.knowledgeBase.currentKnowledgeBase);
const [previewModalVisible, setPreviewModalVisible] = useState(false); const [previewModalVisible, setPreviewModalVisible] = useState(false);
const [selectedDocumentId, setSelectedDocumentId] = useState(null); const [selectedDocumentId, setSelectedDocumentId] = useState(null);
@ -19,6 +20,9 @@ const DocumentList = ({ knowledgeBaseId }) => {
const [displayedItems, setDisplayedItems] = useState([]); const [displayedItems, setDisplayedItems] = useState([]);
const [totalPages, setTotalPages] = useState(1); const [totalPages, setTotalPages] = useState(1);
//
const canEdit = currentKnowledgeBase?.permissions?.can_edit || false;
// //
useEffect(() => { useEffect(() => {
if (!items || items.length === 0) { if (!items || items.length === 0) {
@ -146,12 +150,14 @@ const DocumentList = ({ knowledgeBaseId }) => {
> >
预览 预览
</button> </button>
<button {canEdit && (
className='btn btn-sm btn-outline-danger' <button
onClick={() => handleDeleteDocument(doc.document_id || doc.id)} className='btn btn-sm btn-outline-danger'
> onClick={() => handleDeleteDocument(doc.document_id || doc.id)}
删除 >
</button> 删除
</button>
)}
</div> </div>
</td> </td>
</tr> </tr>

View File

@ -75,7 +75,7 @@ const KnowledgeBaseForm = ({
id='name' id='name'
name='name' name='name'
value={formData.name} value={formData.name}
disabled={!formData.permissions.can_edit} disabled={!(formData.permissions && formData.permissions.can_edit)}
onChange={onInputChange} onChange={onInputChange}
/> />
{formErrors.name && <div className='invalid-feedback'>{formErrors.name}</div>} {formErrors.name && <div className='invalid-feedback'>{formErrors.name}</div>}
@ -91,7 +91,7 @@ const KnowledgeBaseForm = ({
name='desc' name='desc'
rows='3' rows='3'
value={formData.desc} value={formData.desc}
disabled={!formData.permissions.can_edit} disabled={!(formData.permissions && formData.permissions.can_edit)}
onChange={onInputChange} onChange={onInputChange}
></textarea> ></textarea>
{formErrors.desc && <div className='invalid-feedback'>{formErrors.desc}</div>} {formErrors.desc && <div className='invalid-feedback'>{formErrors.desc}</div>}
@ -112,7 +112,7 @@ const KnowledgeBaseForm = ({
value={type.value} value={type.value}
checked={formData.type === type.value} checked={formData.type === type.value}
onChange={onInputChange} onChange={onInputChange}
disabled={!formData.permissions.can_edit} disabled={!(formData.permissions && formData.permissions.can_edit)}
/> />
<label className='form-check-label' htmlFor={`type${type.value}`}> <label className='form-check-label' htmlFor={`type${type.value}`}>
{type.label} {type.label}
@ -127,46 +127,47 @@ const KnowledgeBaseForm = ({
</div> </div>
{/* 仅当不是私有知识库时才显示部门选项 */} {/* 仅当不是私有知识库时才显示部门选项 */}
{formData.type === 'member' || formData.type === 'leader' && ( {formData.type === 'member' ||
<div className='mb-3'> (formData.type === 'leader' && (
<label htmlFor='department' className='form-label'> <div className='mb-3'>
部门 {isAdmin && <span className='text-danger'>*</span>} <label htmlFor='department' className='form-label'>
</label> 部门 {isAdmin && <span className='text-danger'>*</span>}
{isAdmin ? ( </label>
<> {isAdmin ? (
<select <>
className={`form-select ${formErrors.department ? 'is-invalid' : ''}`} <select
id='department' className={`form-select ${formErrors.department ? 'is-invalid' : ''}`}
name='department' id='department'
value={formData.department || ''} name='department'
onChange={onInputChange} value={formData.department || ''}
disabled={isSubmitting} onChange={onInputChange}
> disabled={isSubmitting}
<option value=''>请选择部门</option> >
{departments.map((dept, index) => ( <option value=''>请选择部门</option>
<option key={index} value={dept}> {departments.map((dept, index) => (
{dept} <option key={index} value={dept}>
</option> {dept}
))} </option>
</select> ))}
{formErrors.department && ( </select>
<div className='invalid-feedback'>{formErrors.department}</div> {formErrors.department && (
)} <div className='invalid-feedback'>{formErrors.department}</div>
</> )}
) : ( </>
<> ) : (
<input <>
type='text' <input
className='form-control bg-light' type='text'
id='department' className='form-control bg-light'
name='department' id='department'
value={formData.department || ''} name='department'
readOnly value={formData.department || ''}
/> readOnly
</> />
)} </>
</div> )}
)} </div>
))}
{/* 仅当不是私有知识库时才显示组别选项 */} {/* 仅当不是私有知识库时才显示组别选项 */}
{formData.type === 'member' && ( {formData.type === 'member' && (
@ -246,7 +247,11 @@ const KnowledgeBaseForm = ({
)} )}
<div className='d-flex justify-content-between mt-4'> <div className='d-flex justify-content-between mt-4'>
<button type='submit' className='btn btn-dark' disabled={isSubmitting|| !formData.permissions.can_edit}> <button
type='submit'
className='btn btn-dark'
disabled={isSubmitting || !(formData.permissions && formData.permissions.can_edit)}
>
{isSubmitting ? ( {isSubmitting ? (
<> <>
<span <span
@ -265,7 +270,7 @@ const KnowledgeBaseForm = ({
className='btn btn-outline-danger' className='btn btn-outline-danger'
onClick={onDelete} onClick={onDelete}
// disabled='true' // disabled='true'
disabled={isSubmitting || !formData.permissions.can_edit} disabled={isSubmitting || !(formData.permissions && formData.permissions.can_edit)}
> >
删除知识库 删除知识库
</button> </button>

View File

@ -337,7 +337,7 @@ export default function PendingRequests() {
)} )}
</div> </div>
<div className='request-actions'> <div className='request-actions'>
{(request.status === 'pending' && request.role === 'approver') ? ( {request.status === 'pending' && request.role === 'approver' ? (
<> <>
<button <button
className='btn btn-outline-danger btn-sm' className='btn btn-outline-danger btn-sm'
@ -368,7 +368,11 @@ export default function PendingRequests() {
</> </>
) : ( ) : (
<div className='status-text'> <div className='status-text'>
{request.status === 'approved' ? '已批准' : '已拒绝'} {request.status === 'approved'
? '已批准'
: request.status === 'pending'
? '待处理'
: '已拒绝'}
{request.updated_at && ( {request.updated_at && (
<small className='text-muted d-block'> <small className='text-muted d-block'>
{new Date(request.updated_at).toLocaleDateString()} {new Date(request.updated_at).toLocaleDateString()}

View File

@ -148,7 +148,7 @@ export default function RequestDetailSlideOver({
<div className='p-3 bg-light rounded'>{request.reason || '无申请理由'}</div> <div className='p-3 bg-light rounded'>{request.reason || '无申请理由'}</div>
</div> </div>
</div> </div>
{request.status === 'pending' && ( {request.status === 'pending' && request.role === 'approver' ? (
<div className='slide-over-footer'> <div className='slide-over-footer'>
<button <button
className='btn btn-outline-danger me-2' className='btn btn-outline-danger me-2'
@ -187,7 +187,14 @@ export default function RequestDetailSlideOver({
)} )}
</button> </button>
</div> </div>
)} ) : request.status === 'pending' ? (
<div className='slide-over-footer'>
<div className='alert alert-warning mb-0'>
<span className='badge bg-warning-subtle text-warning me-2'>待处理</span>
等待审批人处理
</div>
</div>
) : null}
</div> </div>
</div> </div>
</> </>

View File

@ -94,7 +94,7 @@ export default function Login() {
></input> ></input>
{submitted && errors.password && <div className='invalid-feedback'>{errors.password}</div>} {submitted && errors.password && <div className='invalid-feedback'>{errors.password}</div>}
</div> </div>
<Link to='#' className='find-password text-body-secondary'> <Link to='#' className='find-password text-body-secondary d-none'>
忘记密码? 忘记密码?
</Link> </Link>
<button type='submit' className='btn btn-dark btn-lg w-100' disabled={isLoading}> <button type='submit' className='btn btn-dark btn-lg w-100' disabled={isLoading}>

View File

@ -102,7 +102,18 @@ const knowledgeBaseSlice = createSlice({
}) })
.addCase(updateKnowledgeBase.fulfilled, (state, action) => { .addCase(updateKnowledgeBase.fulfilled, (state, action) => {
state.loading = false; state.loading = false;
state.currentKnowledgeBase = action.payload; // 只更新基本信息保留其他属性如permissions等
if (state.currentKnowledgeBase) {
state.currentKnowledgeBase = {
...state.currentKnowledgeBase,
name: action.payload.name,
desc: action.payload.desc || action.payload.description,
description: action.payload.description || action.payload.desc,
type: action.payload.type || state.currentKnowledgeBase.type,
department: action.payload.department || state.currentKnowledgeBase.department,
group: action.payload.group || state.currentKnowledgeBase.group,
};
}
state.editStatus = 'successful'; state.editStatus = 'successful';
}) })
.addCase(updateKnowledgeBase.rejected, (state, action) => { .addCase(updateKnowledgeBase.rejected, (state, action) => {