mirror of
https://gh-proxy.org/https://github.com/klinsmaya/SQLBot.git
synced 2026-11-08 01:06:59 +08:00
feat: Support log management (#668)
This commit is contained in:
@@ -9,6 +9,7 @@ from apps.system.api import login, user, aimodel, workspace, assistant, paramete
|
||||
from apps.terminology.api import terminology
|
||||
from apps.settings.api import base
|
||||
|
||||
|
||||
api_router = APIRouter()
|
||||
api_router.include_router(login.router)
|
||||
api_router.include_router(user.router)
|
||||
|
||||
@@ -81,14 +81,15 @@ async def rename(session: SessionDep, chat: RenameChat):
|
||||
)
|
||||
|
||||
|
||||
@router.delete("/{chart_id}", response_model=str, summary=f"{PLACEHOLDER_PREFIX}delete_chat")
|
||||
@router.delete("/{chart_id}/{brief}", response_model=str, summary=f"{PLACEHOLDER_PREFIX}delete_chat")
|
||||
@system_log(LogConfig(
|
||||
operation_type=OperationType.DELETE_QA,
|
||||
operation_detail=OperationDetails.DELETE_QA_DETAILS,
|
||||
module=OperationModules.QA,
|
||||
resource_id_expr="chart_id"
|
||||
resource_id_expr="chart_id",
|
||||
remark_expr="brief"
|
||||
))
|
||||
async def delete(session: SessionDep, chart_id: int):
|
||||
async def delete(session: SessionDep, chart_id: int, brief: str):
|
||||
try:
|
||||
return delete_chat(session=session, chart_id=chart_id)
|
||||
except Exception as e:
|
||||
|
||||
@@ -30,14 +30,15 @@ async def update_resource_api(session: SessionDep, user: CurrentUser, dashboard:
|
||||
return update_resource(session=session, user=user, dashboard=dashboard)
|
||||
|
||||
|
||||
@router.delete("/delete_resource/{resource_id}")
|
||||
@router.delete("/delete_resource/{resource_id}/{name}")
|
||||
@system_log(LogConfig(
|
||||
operation_type=OperationType.DELETE_DASHBOARD,
|
||||
operation_detail=OperationDetails.DELETE_DASHBOARD_DETAILS,
|
||||
module=OperationModules.DASHBOARD,
|
||||
resource_id_expr="resource_id"
|
||||
resource_id_expr="resource_id",
|
||||
remark_expr="name"
|
||||
))
|
||||
async def delete_resource_api(session: SessionDep, resource_id: str):
|
||||
async def delete_resource_api(session: SessionDep, resource_id: str, name: str):
|
||||
return delete_resource(session, resource_id)
|
||||
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@ from ..models.datasource import CoreDatasource, CreateDatasource, TableObj, Core
|
||||
TableSchemaResponse, ColumnSchemaResponse, PreviewResponse
|
||||
from sqlbot_xpack.audit.models.log_model import OperationType, OperationDetails, OperationModules
|
||||
from sqlbot_xpack.audit.schemas.logger_decorator import system_log, LogConfig
|
||||
|
||||
router = APIRouter(tags=["Datasource"], prefix="/datasource")
|
||||
path = settings.EXCEL_PATH
|
||||
|
||||
@@ -107,15 +108,16 @@ async def update(session: SessionDep, trans: Trans, user: CurrentUser, ds: CoreD
|
||||
return await asyncio.to_thread(inner)
|
||||
|
||||
|
||||
@router.post("/delete/{id}", response_model=None, summary=f"{PLACEHOLDER_PREFIX}ds_delete")
|
||||
@router.post("/delete/{id}/{name}", response_model=None, summary=f"{PLACEHOLDER_PREFIX}ds_delete")
|
||||
@require_permissions(permission=SqlbotPermission(type='ds', keyExpression="id"))
|
||||
@system_log(LogConfig(
|
||||
operation_type=OperationType.DELETE_DATASOURCE,
|
||||
operation_detail=OperationDetails.DELETE_DATASOURCE_DETAILS,
|
||||
module=OperationModules.DATASOURCE,
|
||||
resource_id_expr="id"
|
||||
resource_id_expr="id",
|
||||
remark_expr="name"
|
||||
))
|
||||
async def delete(session: SessionDep, id: int = Path(..., description=f"{PLACEHOLDER_PREFIX}ds_id")):
|
||||
async def delete(session: SessionDep, id: int = Path(..., description=f"{PLACEHOLDER_PREFIX}ds_id"), name: str = None):
|
||||
return delete_ds(session, id)
|
||||
|
||||
|
||||
|
||||
@@ -121,5 +121,48 @@
|
||||
},
|
||||
"i18n_authentication": {
|
||||
"record_not_exist": "{msg} record does not exist. Please save it first!"
|
||||
},
|
||||
"i18n_audit": {
|
||||
"success": "Success",
|
||||
"failed": "Failed",
|
||||
"create_qa": "Create Q&A",
|
||||
"delete_qa": "Delete Q&A",
|
||||
"create_datasource": "Create Datasource",
|
||||
"update_datasource": "Edit Datasource",
|
||||
"delete_datasource": "Delete Datasource",
|
||||
"create_dashboard": "Create Dashboard",
|
||||
"update_dashboard": "Edit Dashboard",
|
||||
"delete_dashboard": "Delete Dashboard",
|
||||
"setting_create_user": "Add Member",
|
||||
"setting_delete_user": "Remove Member",
|
||||
"setting_create_rule": "Add Rule Group",
|
||||
"setting_delete_rule": "Remove Rule Group",
|
||||
"setting_update_rule": "Configure Rules",
|
||||
"setting_update_rule_user": "Configure Users",
|
||||
"setting_create_terminology": "Create Terminology",
|
||||
"create_qa_details": "Create Q&A【{resource_name}】",
|
||||
"delete_qa_details": "Delete Q&A【{resource_name}】",
|
||||
"create_datasource_details": "Create Datasource【{resource_name}】",
|
||||
"update_datasource_details": "Edit Datasource【{resource_name}】",
|
||||
"delete_datasource_details": "Delete Datasource【{resource_name}】",
|
||||
"create_dashboard_details": "Create Dashboard【{resource_name}】",
|
||||
"update_dashboard_details": "Edit Dashboard【{resource_name}】",
|
||||
"delete_dashboard_details": "Delete Dashboard【{resource_name}】",
|
||||
"setting_create_user_details": "Add Member【{resource_name}】",
|
||||
"setting_delete_user_details": "Remove Member【{resource_name}】",
|
||||
"setting_create_rule_details": "Add Rule Group【{resource_name}】",
|
||||
"setting_delete_rule_details": "Remove Rule Group【{resource_name}】",
|
||||
"setting_update_rule_details": "Configure Rules【{resource_name}】",
|
||||
"setting_update_rule_user_details": "Configure Users【{resource_name}】",
|
||||
"setting_create_terminology_details": "Create Terminology【{resource_name}】",
|
||||
"system_log": "System Log",
|
||||
"operation_type_name": "Operation Type",
|
||||
"operation_detail_info": "Operation Details",
|
||||
"user_name": "Operating User",
|
||||
"oid_name": "Workspace",
|
||||
"operation_status_name": "Operation Status",
|
||||
"error_message": "Error Message",
|
||||
"ip_address": "IP Address",
|
||||
"create_time": "Operation Time"
|
||||
}
|
||||
}
|
||||
@@ -121,5 +121,48 @@
|
||||
},
|
||||
"i18n_authentication": {
|
||||
"record_not_exist": "{msg} 기록이 존재하지 않습니다. 먼저 저장해 주세요!"
|
||||
},
|
||||
"i18n_audit": {
|
||||
"success": "성공",
|
||||
"failed": "실패",
|
||||
"create_qa": "Q&A 생성",
|
||||
"delete_qa": "Q&A 삭제",
|
||||
"create_datasource": "데이터 소스 생성",
|
||||
"update_datasource": "데이터 소스 편집",
|
||||
"delete_datasource": "데이터 소스 삭제",
|
||||
"create_dashboard": "대시보드 생성",
|
||||
"update_dashboard": "대시보드 편집",
|
||||
"delete_dashboard": "대시보드 삭제",
|
||||
"setting_create_user": "멤버 추가",
|
||||
"setting_delete_user": "멤버 제거",
|
||||
"setting_create_rule": "규칙 그룹 추가",
|
||||
"setting_delete_rule": "규칙 그룹 제거",
|
||||
"setting_update_rule": "규칙 설정",
|
||||
"setting_update_rule_user": "사용자 설정",
|
||||
"setting_create_terminology": "용어 생성",
|
||||
"create_qa_details": "Q&A 생성【{resource_name}】",
|
||||
"delete_qa_details": "Q&A 삭제【{resource_name}】",
|
||||
"create_datasource_details": "데이터 소스 생성【{resource_name}】",
|
||||
"update_datasource_details": "데이터 소스 편집【{resource_name}】",
|
||||
"delete_datasource_details": "데이터 소스 삭제【{resource_name}】",
|
||||
"create_dashboard_details": "대시보드 생성【{resource_name}】",
|
||||
"update_dashboard_details": "대시보드 편집【{resource_name}】",
|
||||
"delete_dashboard_details": "대시보드 삭제【{resource_name}】",
|
||||
"setting_create_user_details": "멤버 추가【{resource_name}】",
|
||||
"setting_delete_user_details": "멤버 제거【{resource_name}】",
|
||||
"setting_create_rule_details": "규칙 그룹 추가【{resource_name}】",
|
||||
"setting_delete_rule_details": "규칙 그룹 제거【{resource_name}】",
|
||||
"setting_update_rule_details": "규칙 설정【{resource_name}】",
|
||||
"setting_update_rule_user_details": "사용자 설정【{resource_name}】",
|
||||
"setting_create_terminology_details": "용어 생성【{resource_name}】",
|
||||
"system_log": "시스템 로그",
|
||||
"operation_type_name": "작업 유형",
|
||||
"operation_detail_info": "작업 상세 정보",
|
||||
"user_name": "작업 사용자",
|
||||
"oid_name": "작업 공간",
|
||||
"operation_status_name": "작업 상태",
|
||||
"error_message": "오류 메시지",
|
||||
"ip_address": "IP 주소",
|
||||
"create_time": "작업 시간"
|
||||
}
|
||||
}
|
||||
@@ -121,5 +121,48 @@
|
||||
},
|
||||
"i18n_authentication": {
|
||||
"record_not_exist": "{msg} 记录不存在,请先保存!"
|
||||
},
|
||||
"i18n_audit": {
|
||||
"success": "成功",
|
||||
"failed": "失败",
|
||||
"create_qa": "新建问数",
|
||||
"delete_qa": "删除问数",
|
||||
"create_datasource": "新建数据源",
|
||||
"update_datasource": "编辑数据源",
|
||||
"delete_datasource": "删除数据源",
|
||||
"create_dashboard": "新建仪表板",
|
||||
"update_dashboard": "编辑仪表板",
|
||||
"delete_dashboard": "删除仪表板",
|
||||
"setting_create_user": "添加成员",
|
||||
"setting_delete_user": "移除成员",
|
||||
"setting_create_rule": "添加规则组",
|
||||
"setting_delete_rule": "移除规则组",
|
||||
"setting_update_rule": "设置规则",
|
||||
"setting_update_rule_user": "设置用户",
|
||||
"setting_create_terminology": "新建术语",
|
||||
"create_qa_details": "新建问数【{resource_name}】",
|
||||
"delete_qa_details": "删除问数【{resource_name}】",
|
||||
"create_datasource_details": "新建数据源【{resource_name}】",
|
||||
"update_datasource_details": "编辑数据源【{resource_name}】",
|
||||
"delete_datasource_details": "删除数据源【{resource_name}】",
|
||||
"create_dashboard_details": "新建仪表板【{resource_name}】",
|
||||
"update_dashboard_details": "编辑仪表板【{resource_name}】",
|
||||
"delete_dashboard_details": "删除仪表板【{resource_name}】",
|
||||
"setting_create_user_details": "添加成员【{resource_name}】",
|
||||
"setting_delete_user_details": "移除成员【{resource_name}】",
|
||||
"setting_create_rule_details": "添加规则组【{resource_name}】",
|
||||
"setting_delete_rule_details": "移除规则组【{resource_name}】",
|
||||
"setting_update_rule_details": "设置规则【{resource_name}】",
|
||||
"setting_update_rule_user_details": "设置用户【{resource_name}】",
|
||||
"setting_create_terminology_details": "新建术语【{resource_name}】",
|
||||
"system_log": "操作日志",
|
||||
"operation_type_name": "操作类型",
|
||||
"operation_detail_info": "操作详情",
|
||||
"user_name": "操作用户",
|
||||
"oid_name": "工作空间",
|
||||
"operation_status_name": "操作状态",
|
||||
"error_message": "错误信息",
|
||||
"ip_address": "IP 地址",
|
||||
"create_time": "操作时间"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,11 +83,11 @@ url = "https://test.pypi.org/simple"
|
||||
explicit = true
|
||||
|
||||
[tool.uv.sources]
|
||||
sqlbot-xpack = { index = "testpypi" }
|
||||
torch = [
|
||||
{ index = "pytorch-cpu", extra = "cpu" },
|
||||
{ index = "pytorch-cu128", extra = "cu128" },
|
||||
]
|
||||
sqlbot-xpack = { path = "../../sqlbot-xpack" }
|
||||
|
||||
[tool.uv]
|
||||
conflicts = [
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import { request } from '@/utils/request'
|
||||
|
||||
export const audit = {
|
||||
getList: (pageNum: any, pageSize: any, params: any) =>
|
||||
request.get(`/system/audit/page/${pageNum}/${pageSize}${params}`),
|
||||
export2Excel: (params: any) =>
|
||||
request.get(`/system/audit/export`, {
|
||||
params,
|
||||
responseType: 'blob',
|
||||
requestOptions: { customError: true },
|
||||
}),
|
||||
}
|
||||
@@ -324,8 +324,8 @@ export const chatApi = {
|
||||
renameChat: (chat_id: number | undefined, brief: string): Promise<string> => {
|
||||
return request.post('/chat/rename', { id: chat_id, brief: brief })
|
||||
},
|
||||
deleteChat: (id: number | undefined): Promise<string> => {
|
||||
return request.delete(`/chat/${id}`)
|
||||
deleteChat: (id: number | undefined, brief: string): Promise<string> => {
|
||||
return request.delete(`/chat/${id}/${brief}`)
|
||||
},
|
||||
analysis: (record_id: number | undefined, controller?: AbortController) => {
|
||||
return request.fetchStream(`/chat/record/${record_id}/analysis`, {}, controller)
|
||||
|
||||
@@ -9,6 +9,6 @@ export const dashboardApi = {
|
||||
update_canvas: (params: any) => request.post('/dashboard/update_canvas', params),
|
||||
check_name: (params: any) => request.post('/dashboard/check_name', params),
|
||||
delete_resource: (params: any) =>
|
||||
request.delete(`/dashboard/delete_resource/${params.id}`, params),
|
||||
request.delete(`/dashboard/delete_resource/${params.id}/${params.name}`, params),
|
||||
move_resource: (params: any) => request.delete(`/dashboard/move_resource/${params.id}`, params),
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ export const datasourceApi = {
|
||||
add: (data: any) => request.post('/datasource/add', data),
|
||||
list: () => request.get('/datasource/list'),
|
||||
update: (data: any) => request.post('/datasource/update', data),
|
||||
delete: (id: number) => request.post(`/datasource/delete/${id}`),
|
||||
delete: (id: number, name: str) => request.post(`/datasource/delete/${id}/${name}`),
|
||||
getTables: (id: number) => request.post(`/datasource/getTables/${id}`),
|
||||
getTablesByConf: (data: any) => request.post('/datasource/getTablesByConf', data),
|
||||
getFields: (id: number, table_name: string) =>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_8570_348)">
|
||||
<path d="M8.00002 15.3334C3.95002 15.3334 0.666687 12.05 0.666687 8.00005C0.666687 3.95005 3.95002 0.666718 8.00002 0.666718C12.05 0.666718 15.3334 3.95005 15.3334 8.00005C15.3334 12.05 12.05 15.3334 8.00002 15.3334Z" fill="#F54A45"/>
|
||||
<path d="M6.01683 5.07416L8.00016 7.05749L9.98349 5.07416C10.046 5.01167 10.1308 4.97656 10.2192 4.97656C10.3075 4.97656 10.3923 5.01167 10.4548 5.07416L10.9262 5.54549C10.9886 5.608 11.0238 5.69277 11.0238 5.78116C11.0238 5.86955 10.9886 5.95432 10.9262 6.01682L8.94283 8.00016L10.9262 9.98349C10.9886 10.046 11.0238 10.1308 11.0238 10.2192C11.0238 10.3075 10.9886 10.3923 10.9262 10.4548L10.4548 10.9262C10.3923 10.9886 10.3075 11.0238 10.2192 11.0238C10.1308 11.0238 10.046 10.9886 9.98349 10.9262L8.00016 8.94282L6.01683 10.9262C5.95432 10.9886 5.86955 11.0238 5.78116 11.0238C5.69277 11.0238 5.608 10.9886 5.54549 10.9262L5.07416 10.4548C5.01167 10.3923 4.97656 10.3075 4.97656 10.2192C4.97656 10.1308 5.01167 10.046 5.07416 9.98349L7.05749 8.00016L5.07416 6.01682C5.01167 5.95432 4.97656 5.86955 4.97656 5.78116C4.97656 5.69277 5.01167 5.608 5.07416 5.54549L5.54549 5.07416C5.608 5.01167 5.69277 4.97656 5.78116 4.97656C5.86955 4.97656 5.95432 5.01167 6.01683 5.07416Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_8570_348">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_7974_67866)">
|
||||
<g clip-path="url(#clip1_7974_67866)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.00002 14C11.3137 14 14 11.3137 14 7.99999C14 4.68628 11.3137 1.99999 8.00002 1.99999C4.68631 1.99999 2.00002 4.68628 2.00002 7.99999C2.00002 11.3137 4.68631 14 8.00002 14ZM8.00002 15.3333C3.94993 15.3333 0.666687 12.0501 0.666687 7.99999C0.666687 3.9499 3.94993 0.666656 8.00002 0.666656C12.0501 0.666656 15.3334 3.9499 15.3334 7.99999C15.3334 12.0501 12.0501 15.3333 8.00002 15.3333ZM7.66669 10.6667H8.33335C8.51745 10.6667 8.66669 10.8159 8.66669 11V11.6667C8.66669 11.8507 8.51745 12 8.33335 12H7.66669C7.48259 12 7.33335 11.8507 7.33335 11.6667V11C7.33335 10.8159 7.48259 10.6667 7.66669 10.6667ZM5.36659 6.1878C5.37506 6.11328 5.38374 6.0513 5.39262 6.00184C5.52258 5.27811 5.85171 4.74588 6.38003 4.40513C6.7938 4.13504 7.30223 3.99999 7.90534 3.99999C8.69781 3.99999 9.35614 4.1919 9.88036 4.57572C10.4046 4.95954 10.6667 5.52815 10.6667 6.28158C10.6667 6.74359 10.5527 7.13274 10.3248 7.44903C10.1916 7.64094 9.93559 7.88616 9.55689 8.18469L9.18345 8.47788C8.98007 8.63781 8.84508 8.82438 8.77845 9.03762C8.76032 9.09581 8.74439 9.20399 8.73065 9.36215C8.7157 9.53443 8.57149 9.66666 8.39857 9.66666H7.6523C7.4682 9.66665 7.31896 9.51742 7.31896 9.33332C7.31896 9.32307 7.31944 9.31282 7.32038 9.30261C7.36331 8.83868 7.40927 8.54511 7.45827 8.42191C7.54944 8.19268 7.78437 7.92881 8.16307 7.63028L8.54703 7.32642C8.67326 7.23047 9.32373 6.77242 9.32373 6.36688C9.32373 5.96133 9.25308 5.81217 9.04946 5.58959C8.84584 5.36702 8.38748 5.29538 7.97372 5.29538C7.56697 5.29538 7.21938 5.4034 7.04932 5.67705C6.97055 5.80379 6.90615 5.939 6.85969 6.07695C6.84142 6.13122 6.82522 6.1995 6.81111 6.28179C6.78367 6.44181 6.64493 6.55878 6.48258 6.55878H5.6978C5.5137 6.55879 5.36446 6.40955 5.36446 6.22545C5.36446 6.21287 5.36517 6.2003 5.36659 6.1878Z" fill="#646A73"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_7974_67866">
|
||||
<rect width="16" height="16" rx="4" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_7974_67866">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_8570_412)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0.666687 7.99999C0.666687 12.0505 3.9495 15.3333 8.00002 15.3333C12.0505 15.3333 15.3334 12.0505 15.3334 7.99999C15.3334 3.94947 12.0505 0.666656 8.00002 0.666656C3.9495 0.666656 0.666687 3.94947 0.666687 7.99999Z" fill="#34C724"/>
|
||||
<path d="M10.7621 5.98431L7.22639 9.51998L5.49305 7.78665C5.43054 7.72416 5.34578 7.68905 5.25739 7.68905C5.169 7.68905 5.08423 7.72416 5.02172 7.78665L4.55072 8.25831C4.48823 8.32082 4.45312 8.40559 4.45312 8.49398C4.45312 8.58237 4.48823 8.66714 4.55072 8.72964L6.99072 11.17C7.05323 11.2325 7.138 11.2676 7.22639 11.2676C7.31477 11.2676 7.39954 11.2325 7.46205 11.17L11.7047 6.92731C11.7672 6.8648 11.8023 6.78003 11.8023 6.69165C11.8023 6.60326 11.7672 6.51849 11.7047 6.45598L11.2334 5.98431C11.1709 5.92182 11.0861 5.88672 10.9977 5.88672C10.9093 5.88672 10.8246 5.92182 10.7621 5.98431Z" fill="white"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_8570_412">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -55,6 +55,7 @@ const routerList = computed(() => {
|
||||
!route.path.includes('prompt') &&
|
||||
!route.path.includes('permission') &&
|
||||
!route.path.includes('preview') &&
|
||||
!route.path.includes('audit') &&
|
||||
route.path !== '/login' &&
|
||||
route.path !== '/admin-login' &&
|
||||
!route.path.includes('/system') &&
|
||||
|
||||
@@ -822,6 +822,40 @@
|
||||
"modelType": {
|
||||
"llm": "Large Language Model"
|
||||
},
|
||||
"audit": {
|
||||
"system_log": "System Log",
|
||||
"search_log": "Search Log",
|
||||
"operation_type": "Operation Type",
|
||||
"operation_details": "Operation Details",
|
||||
"operation_user_name": "Operating User",
|
||||
"operation_status": "Operation Status",
|
||||
"user_name": "User Name",
|
||||
"oid_name": "Workspace",
|
||||
"ip_address": "IP Address",
|
||||
"create_time": "Creation Time",
|
||||
"no_log": "No Logs Available",
|
||||
"all_236_terms": "Export all {msg} log entries?",
|
||||
"export_hint": "Export all logs?",
|
||||
"export": "Export",
|
||||
"success": "Success",
|
||||
"failed": "Failed",
|
||||
"failed_info": "Operation Failure Details",
|
||||
"create_qa": "Create Q&A",
|
||||
"delete_qa": "Delete Q&A",
|
||||
"create_datasource": "Create Datasource",
|
||||
"update_datasource": "Edit Datasource",
|
||||
"delete_datasource": "Delete Datasource",
|
||||
"create_dashboard": "Create Dashboard",
|
||||
"update_dashboard": "Edit Dashboard",
|
||||
"delete_dashboard": "Delete Dashboard",
|
||||
"setting_create_user": "Add Member",
|
||||
"setting_delete_user": "Remove Member",
|
||||
"setting_create_rule": "Add Rule Group",
|
||||
"setting_delete_rule": "Remove Rule Group",
|
||||
"setting_update_rule": "Configure Rules",
|
||||
"setting_update_rule_user": "Configure Users",
|
||||
"setting_create_terminology": "Create Terminology"
|
||||
}
|
||||
"api_key": {
|
||||
"info_tips": "The API Key is your credential for accessing the SQLBot API, with full permissions for your account. Please keep it secure! Do not disclose the API Key in any public channels to avoid security risks from unauthorized use.",
|
||||
"create": "Create",
|
||||
|
||||
@@ -822,6 +822,40 @@
|
||||
"modelType": {
|
||||
"llm": "대형 언어 모델"
|
||||
},
|
||||
"audit": {
|
||||
"system_log": "시스템 로그",
|
||||
"search_log": "검색 로그",
|
||||
"operation_type": "작업 유형",
|
||||
"operation_details": "작업 상세",
|
||||
"operation_user_name": "작업 사용자",
|
||||
"operation_status": "작업 상태",
|
||||
"user_name": "사용자 이름",
|
||||
"oid_name": "작업 공간",
|
||||
"ip_address": "IP 주소",
|
||||
"create_time": "생성 시간",
|
||||
"no_log": "로그 없음",
|
||||
"all_236_terms": "전체 {msg}개의 로그를 내보내시겠습니까?",
|
||||
"export_hint": "모든 로그를 내보내시겠습니까?",
|
||||
"export": "내보내기",
|
||||
"success": "성공",
|
||||
"failed": "실패",
|
||||
"failed_info": "작업 실패 상세",
|
||||
"create_qa": "Q&A 생성",
|
||||
"delete_qa": "Q&A 삭제",
|
||||
"create_datasource": "데이터 소스 생성",
|
||||
"update_datasource": "데이터 소스 편집",
|
||||
"delete_datasource": "데이터 소스 삭제",
|
||||
"create_dashboard": "대시보드 생성",
|
||||
"update_dashboard": "대시보드 편집",
|
||||
"delete_dashboard": "대시보드 삭제",
|
||||
"setting_create_user": "멤버 추가",
|
||||
"setting_delete_user": "멤버 제거",
|
||||
"setting_create_rule": "규칙 그룹 추가",
|
||||
"setting_delete_rule": "규칙 그룹 제거",
|
||||
"setting_update_rule": "규칙 설정",
|
||||
"setting_update_rule_user": "사용자 설정",
|
||||
"setting_create_terminology": "용어 생성"
|
||||
}
|
||||
"api_key": {
|
||||
"info_tips": "API 키는 SQLBot API에 액세스하는 비밀키로 계정의 모든 권한을 갖고 있습니다. 꼭 안전하게 보관하세요! 외부 채널에 어떠한 방식으로도 API 키를 공개하지 마시고, 타인이 악용하여 보안 위협이 발생하는 것을 방지하세요.",
|
||||
"create": "생성",
|
||||
|
||||
@@ -823,6 +823,40 @@
|
||||
"modelType": {
|
||||
"llm": "大语言模型"
|
||||
},
|
||||
"audit": {
|
||||
"system_log": "操作日志",
|
||||
"search_log": "搜索日志",
|
||||
"operation_type": "操作类型",
|
||||
"operation_details": "操作详情",
|
||||
"operation_user_name": "操作用户",
|
||||
"operation_status": "操作状态",
|
||||
"user_name": "操作日志",
|
||||
"oid_name": "工作空间",
|
||||
"ip_address": "IP 地址",
|
||||
"create_time": "创建时间",
|
||||
"no_log": "暂无日志",
|
||||
"all_236_terms": "是否导出全部 {msg} 条日志?",
|
||||
"export_hint": "是否导出全部日志?",
|
||||
"export": "导出",
|
||||
"success": "成功",
|
||||
"failed": "失败",
|
||||
"failed_info": "操作失败详情",
|
||||
"create_qa": "新建问数",
|
||||
"delete_qa": "删除问数",
|
||||
"create_datasource": "新建数据源",
|
||||
"update_datasource": "编辑数据源",
|
||||
"delete_datasource": "删除数据源",
|
||||
"create_dashboard": "新建仪表板",
|
||||
"update_dashboard": "编辑仪表板",
|
||||
"delete_dashboard": "删除仪表板",
|
||||
"setting_create_user": "添加成员",
|
||||
"setting_delete_user": "移除成员",
|
||||
"setting_create_rule": "添加规则组",
|
||||
"setting_delete_rule": "移除规则组",
|
||||
"setting_update_rule": "设置规则",
|
||||
"setting_update_rule_user": "设置用户",
|
||||
"setting_create_terminology": "新建术语"
|
||||
}
|
||||
"api_key": {
|
||||
"info_tips": "API Key 是您访问 SQLBot API 的密钥,具有账户的完全权限,请您务必妥善保管!不要以任何方式公开 API Key 到外部渠道,避免被他人利用造成安全威胁。",
|
||||
"create": "创建",
|
||||
|
||||
@@ -18,6 +18,7 @@ import Member from '@/views/system/member/index.vue'
|
||||
import Professional from '@/views/system/professional/index.vue'
|
||||
import Training from '@/views/system/training/index.vue'
|
||||
import Prompt from '@/views/system/prompt/index.vue'
|
||||
import Audit from '@/views/system/audit/index.vue'
|
||||
import Appearance from '@/views/system/appearance/index.vue'
|
||||
import Parameter from '@/views/system/parameter/index.vue'
|
||||
import Authentication from '@/views/system/authentication/index.vue'
|
||||
@@ -130,6 +131,12 @@ export const routes = [
|
||||
component: Prompt,
|
||||
meta: { title: t('prompt.customize_prompt_words') },
|
||||
},
|
||||
{
|
||||
path: '/set/audit',
|
||||
name: 'audit',
|
||||
component: Audit,
|
||||
meta: { title: t('audit.system_log') },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ function handleCommand(command: string | number | object, chat: Chat) {
|
||||
}).then(() => {
|
||||
_loading.value = true
|
||||
chatApi
|
||||
.deleteChat(chat.id)
|
||||
.deleteChat(chat.id, chat.brief)
|
||||
.then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
|
||||
@@ -230,7 +230,7 @@ const operation = (opt: string, data: SQTreeNode) => {
|
||||
autofocus: false,
|
||||
showClose: false,
|
||||
}).then(() => {
|
||||
dashboardApi.delete_resource({ id: data.id }).then(() => {
|
||||
dashboardApi.delete_resource({ id: data.id, name: data.name }).then(() => {
|
||||
ElMessage.success(t('dashboard.delete_success'))
|
||||
getTree()
|
||||
dashboardStore.canvasDataInit()
|
||||
|
||||
@@ -159,7 +159,7 @@ const deleteHandler = (item: any) => {
|
||||
''
|
||||
),
|
||||
}).then(() => {
|
||||
datasourceApi.delete(item.id).then(() => {
|
||||
datasourceApi.delete(item.id, item.name).then(() => {
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: t('dashboard.delete_success'),
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import icon_export_outlined from '@/assets/svg/icon_export_outlined.svg'
|
||||
import { formatTimestamp } from '@/utils/date'
|
||||
import EmptyBackground from '@/views/dashboard/common/EmptyBackground.vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { convertFilterText, FilterText } from '@/components/filter-text'
|
||||
import { DrawerMain } from '@/components/drawer-main'
|
||||
import iconFilter from '@/assets/svg/icon-filter_outlined.svg'
|
||||
import { audit } from '@/api/audit.ts'
|
||||
import { workspaceList } from '@/api/workspace.ts'
|
||||
import { userApi } from '@/api/auth.ts'
|
||||
import icon_success from '@/assets/svg/icon_success.svg'
|
||||
import icon_error from '@/assets/svg/icon_error.svg'
|
||||
import icon_issue from '@/assets/svg/icon_issue.svg'
|
||||
|
||||
const { t } = useI18n()
|
||||
const multipleSelectionAll = ref<any[]>([])
|
||||
const keywords = ref('')
|
||||
const oldKeywords = ref('')
|
||||
const searchLoading = ref(false)
|
||||
const drawerMainRef = ref()
|
||||
|
||||
onMounted(() => {
|
||||
search()
|
||||
initWorkspace()
|
||||
initUsers()
|
||||
})
|
||||
const multipleTableRef = ref()
|
||||
const fieldList = ref<any>([])
|
||||
const pageInfo = reactive({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
})
|
||||
|
||||
const curErrorInfo = ref()
|
||||
|
||||
const state = reactive<any>({
|
||||
conditions: [],
|
||||
filterTexts: [],
|
||||
})
|
||||
|
||||
const exportExcel = () => {
|
||||
ElMessageBox.confirm(t('audit.all_236_terms', { msg: pageInfo.total }), {
|
||||
confirmButtonType: 'primary',
|
||||
confirmButtonText: t('audit.export'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
customClass: 'confirm-no_icon',
|
||||
autofocus: false,
|
||||
}).then(() => {
|
||||
searchLoading.value = true
|
||||
audit
|
||||
.export2Excel(configParams())
|
||||
.then((res) => {
|
||||
const blob = new Blob([res], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
})
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = `${t('audit.system_log')}.xlsx`
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
})
|
||||
.catch(async (error) => {
|
||||
if (error.response) {
|
||||
try {
|
||||
let text = await error.response.data.text()
|
||||
try {
|
||||
text = JSON.parse(text)
|
||||
} finally {
|
||||
ElMessage({
|
||||
message: text,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error processing error response:', e)
|
||||
}
|
||||
} else {
|
||||
console.error('Other error:', error)
|
||||
ElMessage({
|
||||
message: error,
|
||||
type: 'error',
|
||||
showClose: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
searchLoading.value = false
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const toggleRowLoading = ref(false)
|
||||
|
||||
const configParams = () => {
|
||||
let str = ''
|
||||
if (keywords.value) {
|
||||
str += `name=${keywords.value}`
|
||||
}
|
||||
|
||||
state.conditions.forEach((ele: any) => {
|
||||
ele.value.forEach((itx: any) => {
|
||||
str += str ? `__${itx}` : `${ele.field}=${itx}`
|
||||
})
|
||||
})
|
||||
|
||||
if (str.length) {
|
||||
str = `?${str}`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
const search = () => {
|
||||
searchLoading.value = true
|
||||
oldKeywords.value = keywords.value
|
||||
audit
|
||||
.getList(pageInfo.currentPage, pageInfo.pageSize, configParams())
|
||||
.then((res) => {
|
||||
toggleRowLoading.value = true
|
||||
fieldList.value = res.data
|
||||
pageInfo.total = res.total_count
|
||||
searchLoading.value = false
|
||||
})
|
||||
.finally(() => {
|
||||
searchLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const handleSizeChange = (val: number) => {
|
||||
pageInfo.currentPage = 1
|
||||
pageInfo.pageSize = val
|
||||
search()
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val: number) => {
|
||||
pageInfo.currentPage = val
|
||||
search()
|
||||
}
|
||||
const rowInfoDialog = ref(false)
|
||||
|
||||
const onErrorInfoOpen = (info: string) => {
|
||||
rowInfoDialog.value = true
|
||||
curErrorInfo.value = info
|
||||
}
|
||||
|
||||
const onErrorInfoClose = () => {
|
||||
rowInfoDialog.value = false
|
||||
}
|
||||
const operationTypeOptions = [
|
||||
{ id: 'create_qa', name: t('audit.create_qa') },
|
||||
{ id: 'delete_qa', name: t('audit.delete_qa') },
|
||||
{ id: 'create_datasource', name: t('audit.create_datasource') },
|
||||
{ id: 'update_datasource', name: t('audit.update_datasource') },
|
||||
{ id: 'delete_datasource', name: t('audit.delete_datasource') },
|
||||
{ id: 'create_dashboard', name: t('audit.create_dashboard') },
|
||||
{ id: 'update_dashboard', name: t('audit.update_dashboard') },
|
||||
{ id: 'delete_dashboard', name: t('audit.delete_dashboard') },
|
||||
{ id: 'setting_create_user', name: t('audit.setting_create_user') },
|
||||
{ id: 'setting_delete_user', name: t('audit.setting_delete_user') },
|
||||
{ id: 'setting_create_rule', name: t('audit.setting_create_rule') },
|
||||
{ id: 'setting_delete_rule', name: t('audit.setting_delete_rule') },
|
||||
{ id: 'setting_update_rule', name: t('audit.setting_update_rule') },
|
||||
{ id: 'setting_update_rule_user', name: t('audit.setting_update_rule_user') },
|
||||
{ id: 'setting_create_terminology', name: t('audit.setting_create_terminology') },
|
||||
]
|
||||
const filterOption = ref<any[]>([
|
||||
{
|
||||
type: 'select',
|
||||
option: operationTypeOptions,
|
||||
field: 'opt_type_list',
|
||||
title: t('audit.operation_type'),
|
||||
operate: 'in',
|
||||
property: { placeholder: t('common.empty') + t('audit.operation_type') },
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
option: [],
|
||||
field: 'uid_list',
|
||||
title: t('audit.operation_user_name'),
|
||||
operate: 'in',
|
||||
property: { placeholder: t('common.empty') + t('audit.operation_user_name') },
|
||||
},
|
||||
{
|
||||
type: 'select',
|
||||
option: [],
|
||||
field: 'oid_list',
|
||||
title: t('audit.operation_user_name'),
|
||||
operate: 'in',
|
||||
property: { placeholder: t('common.empty') + t('audit.oid_name') },
|
||||
},
|
||||
{
|
||||
type: 'enum',
|
||||
option: [
|
||||
{ id: 'success', name: t('audit.success') },
|
||||
{ id: 'failed', name: t('audit.failed') },
|
||||
],
|
||||
field: 'log_status',
|
||||
title: t('user.user_status'),
|
||||
operate: 'in',
|
||||
},
|
||||
])
|
||||
|
||||
const fillFilterText = () => {
|
||||
const textArray = state.conditions?.length
|
||||
? convertFilterText(state.conditions, filterOption.value)
|
||||
: []
|
||||
state.filterTexts = [...textArray]
|
||||
Object.assign(state.filterTexts, textArray)
|
||||
}
|
||||
const searchCondition = (conditions: any) => {
|
||||
state.conditions = conditions
|
||||
fillFilterText()
|
||||
search()
|
||||
drawerMainClose()
|
||||
}
|
||||
|
||||
const clearFilter = (params?: number) => {
|
||||
let index = params ? params : 0
|
||||
if (isNaN(index)) {
|
||||
state.filterTexts = []
|
||||
} else {
|
||||
state.filterTexts.splice(index, 1)
|
||||
}
|
||||
drawerMainRef.value.clearFilter(index)
|
||||
}
|
||||
|
||||
const drawerMainOpen = async () => {
|
||||
drawerMainRef.value.init()
|
||||
}
|
||||
|
||||
const drawerMainClose = () => {
|
||||
drawerMainRef.value.close()
|
||||
}
|
||||
const initWorkspace = () => {
|
||||
workspaceList().then((res) => {
|
||||
filterOption.value[2].option = [...res]
|
||||
})
|
||||
}
|
||||
|
||||
const initUsers = () => {
|
||||
userApi.pager(1, 10000).then((res: any) => {
|
||||
filterOption.value[1].option = [{ id: 1, name: 'Administrator' }, ...res.items]
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-loading="searchLoading" class="professional">
|
||||
<div class="tool-left">
|
||||
<span class="page-title">{{ $t('audit.system_log') }}</span>
|
||||
<div class="tool-row">
|
||||
<el-button secondary @click="exportExcel">
|
||||
<template #icon>
|
||||
<icon_export_outlined />
|
||||
</template>
|
||||
{{ $t('professional.export_all') }}
|
||||
</el-button>
|
||||
<el-button class="no-margin" secondary @click="drawerMainOpen">
|
||||
<template #icon>
|
||||
<iconFilter></iconFilter>
|
||||
</template>
|
||||
{{ $t('user.filter') }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!searchLoading"
|
||||
class="table-content"
|
||||
:class="multipleSelectionAll?.length && 'show-pagination_height'"
|
||||
>
|
||||
<filter-text
|
||||
:total="pageInfo.total"
|
||||
:filter-texts="state.filterTexts"
|
||||
@clear-filter="clearFilter"
|
||||
/>
|
||||
<div class="preview-or-schema">
|
||||
<el-table ref="multipleTableRef" :data="fieldList" style="width: 100%">
|
||||
<el-table-column prop="word" :label="$t('audit.operation_type')" width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.operation_type_name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('audit.operation_details')" min-width="200"
|
||||
><template #default="scope">
|
||||
{{ scope.row.operation_detail_info }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('audit.operation_user_name')" min-width="120"
|
||||
><template #default="scope">
|
||||
{{ scope.row.user_name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="word" :label="$t('audit.oid_name')" width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.oid_name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="word" :label="$t('audit.operation_status')" width="100">
|
||||
<template #default="scope">
|
||||
<div style="display: flex; align-items: center">
|
||||
<el-icon size="16" style="margin-right: 4px">
|
||||
<icon_success v-if="scope.row.operation_status === 'success'"></icon_success>
|
||||
<icon_error v-if="scope.row.operation_status === 'failed'"></icon_error>
|
||||
</el-icon>
|
||||
{{ scope.row.operation_status_name }}
|
||||
<el-icon
|
||||
v-if="scope.row.operation_status === 'failed'"
|
||||
size="16"
|
||||
style="margin-left: 4px; cursor: pointer"
|
||||
@click="onErrorInfoOpen(scope.row.error_message)"
|
||||
>
|
||||
<icon_issue></icon_issue>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="word" :label="$t('audit.ip_address')" width="120">
|
||||
<template #default="scope">
|
||||
{{ scope.row.ip_address }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="create_time" sortable :label="$t('audit.create_time')" width="180">
|
||||
<template #default="scope">
|
||||
<span>{{ formatTimestamp(scope.row.create_time, 'YYYY-MM-DD HH:mm:ss') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #empty>
|
||||
<EmptyBackground
|
||||
v-if="!oldKeywords && !fieldList.length"
|
||||
:description="$t('audit.no_log')"
|
||||
img-type="noneWhite"
|
||||
/>
|
||||
|
||||
<EmptyBackground
|
||||
v-if="!!oldKeywords && !fieldList.length"
|
||||
:description="$t('datasource.relevant_content_found')"
|
||||
img-type="tree"
|
||||
/>
|
||||
</template>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="fieldList.length" class="pagination-container">
|
||||
<el-pagination
|
||||
v-model:current-page="pageInfo.currentPage"
|
||||
v-model:page-size="pageInfo.pageSize"
|
||||
:page-sizes="[10, 20, 30]"
|
||||
:background="true"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageInfo.total"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-drawer
|
||||
v-model="rowInfoDialog"
|
||||
:title="$t('audit.failed_info')"
|
||||
destroy-on-close
|
||||
:before-close="onErrorInfoClose"
|
||||
direction="btt"
|
||||
size="80%"
|
||||
modal-class="professional-term_drawer"
|
||||
>
|
||||
<div style="width: 100%; height: 100%">
|
||||
{{ curErrorInfo }}
|
||||
</div>
|
||||
</el-drawer>
|
||||
<drawer-main
|
||||
ref="drawerMainRef"
|
||||
:filter-options="filterOption"
|
||||
@trigger-filter="searchCondition"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.no-margin {
|
||||
margin: 0;
|
||||
}
|
||||
.professional {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.datasource-yet {
|
||||
padding-bottom: 0;
|
||||
height: auto;
|
||||
padding-top: 200px;
|
||||
}
|
||||
|
||||
:deep(.ed-table__cell) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tool-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16px;
|
||||
|
||||
.page-title {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
line-height: 28px;
|
||||
}
|
||||
.tool-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination-container {
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.table-content {
|
||||
max-height: calc(100% - 104px);
|
||||
overflow-y: auto;
|
||||
|
||||
&.show-pagination_height {
|
||||
max-height: calc(100% - 165px);
|
||||
}
|
||||
|
||||
.preview-or-schema {
|
||||
.field-comment_d {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 24px;
|
||||
}
|
||||
.notes-in_table {
|
||||
max-width: 100%;
|
||||
display: -webkit-box;
|
||||
max-height: 66px;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3; /* 限制行数为3 */
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-word;
|
||||
}
|
||||
.ed-icon {
|
||||
color: #646a73;
|
||||
}
|
||||
|
||||
.user-status-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
height: 24px;
|
||||
|
||||
.ed-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.field-comment {
|
||||
height: 24px;
|
||||
|
||||
.ed-icon {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
margin-top: 4px;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
background-color: #1f23291a;
|
||||
position: absolute;
|
||||
border-radius: 6px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
transform: translate(-50%, -50%);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:not(.not-allow):hover {
|
||||
&::after {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
&.not-allow {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
.ed-icon + .ed-icon {
|
||||
margin-left: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.preview-num {
|
||||
margin: 12px 0;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-select {
|
||||
position: absolute;
|
||||
height: 64px;
|
||||
width: calc(100% + 48px);
|
||||
left: -24px;
|
||||
bottom: -16px;
|
||||
border-top: 1px solid #1f232926;
|
||||
display: flex;
|
||||
background-color: #fff;
|
||||
align-items: center;
|
||||
padding-left: 24px;
|
||||
background-color: #fff;
|
||||
z-index: 10;
|
||||
|
||||
.danger-button {
|
||||
border: 1px solid var(--ed-color-danger);
|
||||
color: var(--ed-color-danger);
|
||||
border-radius: var(--ed-border-radius-base);
|
||||
min-width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 0 16px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.primary-button {
|
||||
border: 1px solid var(--ed-color-primary);
|
||||
color: var(--ed-color-primary);
|
||||
border-radius: var(--ed-border-radius-base);
|
||||
min-width: 80px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
margin: 0 16px;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.selected {
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
color: #646a73;
|
||||
margin-right: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
.professional-term_drawer {
|
||||
.ed-form-item--label-top .ed-form-item__label {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.ed-form-item__label {
|
||||
color: #646a73;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
line-height: 22px;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.professional-add_drawer {
|
||||
.no-error.no-error {
|
||||
.ed-form-item__error {
|
||||
display: none;
|
||||
}
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.ed-textarea__inner {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.ed-form-item__label:has(.btn) {
|
||||
padding-right: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.scrollbar-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-right: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.synonyms-list {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: calc(100% + 4px);
|
||||
height: calc(100vh - 390px);
|
||||
}
|
||||
|
||||
.btn {
|
||||
margin-left: auto;
|
||||
height: 26px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0 4px;
|
||||
border-radius: 6px;
|
||||
margin-right: -4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background-color: #1f23291a;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user