提交
This commit is contained in:
1 parent
912ada02eb
commit
239d0a03d7
24 files changed
+841
-314
No files matched your search
+1
-1
@@ -1 +1 @@
|
||||
1098
|
||||
69671
|
||||
@@ -45,6 +45,8 @@ async def list_pilot_release_reviews(
|
||||
scheduleStatus: Optional[str] = None,
|
||||
qcStatus: Optional[str] = None,
|
||||
bizType: Optional[str] = None,
|
||||
infusionTimeStart: Optional[str] = None,
|
||||
infusionTimeEnd: Optional[str] = None,
|
||||
db: Session = Depends(get_db),
|
||||
user: dict = Depends(require_menu_code("biz:outboundReleaseReview:list")),
|
||||
):
|
||||
@@ -58,6 +60,8 @@ async def list_pilot_release_reviews(
|
||||
schedule_status=scheduleStatus,
|
||||
qc_status=qcStatus,
|
||||
biz_type=bizType,
|
||||
infusion_time_start=infusionTimeStart,
|
||||
infusion_time_end=infusionTimeEnd,
|
||||
page=page,
|
||||
page_size=pageSize,
|
||||
user_info=user,
|
||||
|
||||
@@ -128,6 +128,28 @@ def map_payments_by_schedule_id(
|
||||
return {row.infusion_schedule_id: _enrich_payment(db, row) for row in rows}
|
||||
|
||||
|
||||
def map_payments_by_schedule_ids(
|
||||
db: Session,
|
||||
*,
|
||||
schedule_ids: List[str],
|
||||
tenant_no: str,
|
||||
) -> Dict[str, Dict[str, Any]]:
|
||||
"""按输注计划 ID 批量索引缴费记录(列表页展示用,不做关联 enrich)"""
|
||||
unique_ids = [sid for sid in {sid for sid in schedule_ids if sid}]
|
||||
if not unique_ids:
|
||||
return {}
|
||||
rows = (
|
||||
db.query(BizPilotPayment)
|
||||
.filter(
|
||||
BizPilotPayment.infusion_schedule_id.in_(unique_ids),
|
||||
BizPilotPayment.tenant_no == tenant_no,
|
||||
BizPilotPayment.del_flag == 0,
|
||||
)
|
||||
.all()
|
||||
)
|
||||
return {row.infusion_schedule_id: _row_to_dict(row) for row in rows}
|
||||
|
||||
|
||||
# ─── create (auto) ────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import math
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy import func, or_
|
||||
from sqlalchemy import and_, func, or_
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from app.core.utils import generate_uuid
|
||||
@@ -118,6 +118,8 @@ def list_reviews_paginated(
|
||||
schedule_status: Optional[str] = None,
|
||||
qc_status: Optional[str] = None,
|
||||
biz_type: Optional[str] = None,
|
||||
infusion_time_start: Optional[str] = None,
|
||||
infusion_time_end: Optional[str] = None,
|
||||
page: int = 1,
|
||||
page_size: int = 10,
|
||||
user_info=None,
|
||||
@@ -212,6 +214,24 @@ def list_reviews_paginated(
|
||||
if qs:
|
||||
q0 = q0.filter(BizPilotInfusionSchedule.qc_status == qs)
|
||||
|
||||
# ── 输注时间范围 ──
|
||||
time_bounds = []
|
||||
if (infusion_time_start or "").strip():
|
||||
try:
|
||||
start_dt = datetime.strptime(infusion_time_start.strip()[:10], "%Y-%m-%d")
|
||||
time_bounds.append(BizPilotInfusionSchedule.infusion_time >= start_dt)
|
||||
except ValueError:
|
||||
pass
|
||||
if (infusion_time_end or "").strip():
|
||||
try:
|
||||
end_dt = datetime.strptime(infusion_time_end.strip()[:10], "%Y-%m-%d")
|
||||
end_dt = end_dt.replace(hour=23, minute=59, second=59)
|
||||
time_bounds.append(BizPilotInfusionSchedule.infusion_time <= end_dt)
|
||||
except ValueError:
|
||||
pass
|
||||
if time_bounds:
|
||||
q0 = q0.filter(or_(and_(*time_bounds), BizPilotInfusionSchedule.infusion_time.is_(None)))
|
||||
|
||||
# ── review_conclusion 过滤 ──
|
||||
rc = (review_conclusion or "").strip()
|
||||
if rc == "pending":
|
||||
|
||||
@@ -385,6 +385,21 @@ def list_infusion_schedules_mgmt(
|
||||
)
|
||||
)
|
||||
|
||||
schedule_ids = [item["id"] for item in items if item.get("id")]
|
||||
if schedule_ids:
|
||||
from app.crud import clinical_payment
|
||||
|
||||
payment_map = clinical_payment.map_payments_by_schedule_ids(
|
||||
db, schedule_ids=schedule_ids, tenant_no=tenant_no
|
||||
)
|
||||
for item in items:
|
||||
payment = payment_map.get(item["id"])
|
||||
if not payment:
|
||||
continue
|
||||
item["payment"] = payment
|
||||
item["amount"] = payment.get("amount")
|
||||
item["payStatus"] = payment.get("payStatus")
|
||||
|
||||
return {
|
||||
"list": items,
|
||||
"pagination": {"page": page, "pageSize": page_size, "total": total},
|
||||
|
||||
@@ -12,6 +12,8 @@ class OutboundReviewListParams(BaseModel):
|
||||
reviewConclusion: Optional[str] = None
|
||||
scheduleStatus: Optional[str] = None
|
||||
qcStatus: Optional[str] = None
|
||||
infusionTimeStart: Optional[str] = None
|
||||
infusionTimeEnd: Optional[str] = None
|
||||
|
||||
|
||||
class OutboundReviewSubmitBody(BaseModel):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Graph Report - src (2026-05-28)
|
||||
|
||||
## Corpus Check
|
||||
- Large corpus: 672 files · ~417,392 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
- Large corpus: 672 files · ~417,381 words. Semantic extraction will be expensive (many Claude tokens). Consider running on a subfolder, or use --no-semantic to run AST-only.
|
||||
|
||||
## Summary
|
||||
- 1934 nodes · 2494 edges · 50 communities detected
|
||||
|
||||
@@ -329,7 +329,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/index.vue": 1779864070.4019275,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/production/process-category/OutputTypeTab.vue": 1779864078.964701,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/index/index.vue": 1779525781.6949234,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779965464.3573413,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue": 1779965568.1579607,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/QcWorkOrderTaskSubList.vue": 1779948870.746249,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/OutboundQcViewDialog.vue": 1779889633.846836,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception-check-item/index.vue": 1779857055.2939715,
|
||||
@@ -339,7 +339,7 @@
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material/QcMaterialUsageTab.vue": 1779928630.307365,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/sample-reception/index.vue": 1779884110.7197206,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-task/index.vue": 1779926469.2154074,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1779857044.9472554,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue": 1779966532.6566818,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseReviewDialog.vue": 1779701230.688804,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/StorageReleaseViewDialog.vue": 1779790362.6831455,
|
||||
"/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-material-usage/index.vue": 1779864358.234948,
|
||||
|
||||
@@ -160,6 +160,8 @@ export function fetchOutboundReleaseReviewList(params?: {
|
||||
keyword?: string
|
||||
/** pilot_service | clinical_application */
|
||||
bizType?: string
|
||||
infusionTimeStart?: string
|
||||
infusionTimeEnd?: string
|
||||
}) {
|
||||
return request.get<PageList<OutboundReleaseReview>>({
|
||||
url: '/web/admin/pilot-release-reviews',
|
||||
|
||||
@@ -185,31 +185,58 @@
|
||||
v-for="(att, attIdx) in row.attachments"
|
||||
:key="attIdx"
|
||||
class="paper-att-card"
|
||||
:class="{
|
||||
'paper-att-card--image': isImageFile(att),
|
||||
'paper-att-card--video': isVideoFile(att)
|
||||
}"
|
||||
:title="att.fileName"
|
||||
>
|
||||
<el-icon class="paper-att-card__icon"><Document /></el-icon>
|
||||
<span class="paper-att-card__name" :title="att.fileName">{{
|
||||
att.fileName || '附件'
|
||||
}}</span>
|
||||
<div
|
||||
class="paper-att-card__preview"
|
||||
:class="{
|
||||
'is-clickable': isImageFile(att) || isVideoFile(att)
|
||||
}"
|
||||
@click="
|
||||
(isImageFile(att) || isVideoFile(att)) && handlePreviewPaperAttachment(att)
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-if="isImageFile(att)"
|
||||
:src="fullFileUrl(att.attachmentUrl)"
|
||||
:alt="att.fileName || '附件'"
|
||||
/>
|
||||
<template v-else-if="isVideoFile(att)">
|
||||
<div class="paper-att-card__type-icon paper-att-card__type-icon--video">
|
||||
<el-icon><VideoPlay /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-icon class="paper-att-card__type-icon paper-att-card__type-icon--doc">
|
||||
<Document />
|
||||
</el-icon>
|
||||
<span class="paper-att-card__name">{{ att.fileName || '附件' }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="paper-att-card__actions">
|
||||
<el-icon
|
||||
v-if="isImageFile(att)"
|
||||
v-if="isImageFile(att) || isVideoFile(att)"
|
||||
class="paper-att-card__action"
|
||||
title="预览"
|
||||
@click="handlePreviewPaperAttachment(att)"
|
||||
@click.stop="handlePreviewPaperAttachment(att)"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action"
|
||||
title="下载"
|
||||
@click="handleDownloadPaperAttachment(att)"
|
||||
@click.stop="handleDownloadPaperAttachment(att)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action paper-att-card__action--danger"
|
||||
title="删除"
|
||||
@click="removePaperAttachment($index, attIdx)"
|
||||
@click.stop="removePaperAttachment($index, attIdx)"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
@@ -357,7 +384,8 @@
|
||||
Search,
|
||||
Document,
|
||||
Download,
|
||||
View
|
||||
View,
|
||||
VideoPlay
|
||||
} from '@element-plus/icons-vue'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { fetchCellProductList } from '@/api/cell-product'
|
||||
@@ -874,6 +902,11 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.el-upload) {
|
||||
display: block;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card {
|
||||
@@ -884,17 +917,62 @@
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-att-card__icon {
|
||||
color: #0284c7;
|
||||
font-size: 18px;
|
||||
.paper-att-card__preview {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card--image,
|
||||
.paper-att-card--video {
|
||||
.paper-att-card__preview {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card__type-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&--doc {
|
||||
color: #0284c7;
|
||||
}
|
||||
|
||||
&--video {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background: #ede9fe;
|
||||
color: #7c3aed;
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card__name {
|
||||
@@ -944,6 +1022,10 @@
|
||||
}
|
||||
|
||||
.paper-att-card--trigger {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 6px 4px;
|
||||
border: 1px dashed #a7f3d0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
@@ -961,14 +1043,20 @@
|
||||
}
|
||||
|
||||
.paper-att-card__trigger-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #10b981;
|
||||
margin-bottom: 2px;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.paper-att-card--trigger span {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_storage_release_review_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/storage-release-review/index.vue", "source_location": "L1"}], "edges": [], "raw_calls": []}
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"nodes": [{"id": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "label": "index.vue", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1"}, {"id": "qc_work_order_index_formattempcellproductfield", "label": "formatTempCellProductField()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L840"}, {"id": "qc_work_order_index_handlereceptionattachmentpreview", "label": "handleReceptionAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1217"}, {"id": "qc_work_order_index_handlereceptionattachmentdownload", "label": "handleReceptionAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1222"}, {"id": "qc_work_order_index_taskattachmentfullurl", "label": "taskAttachmentFullUrl()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1226"}, {"id": "qc_work_order_index_handletaskattachmentpreview", "label": "handleTaskAttachmentPreview()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1233"}, {"id": "qc_work_order_index_handletaskattachmentdownload", "label": "handleTaskAttachmentDownload()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1238"}, {"id": "qc_work_order_index_formatconfiguredlabitems", "label": "formatConfiguredLabItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1254"}, {"id": "qc_work_order_index_formatconfiguredthirditems", "label": "formatConfiguredThirdItems()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1260"}, {"id": "qc_work_order_index_buildtasktypesections", "label": "buildTaskTypeSections()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1271"}, {"id": "qc_work_order_index_buildconfiguredonlysection", "label": "buildConfiguredOnlySection()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1360"}, {"id": "qc_work_order_index_formatdetectmethods", "label": "formatDetectMethods()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1378"}, {"id": "qc_work_order_index_formatmetric", "label": "formatMetric()", "file_type": "code", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1385"}], "edges": [{"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_formattempcellproductfield", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L840", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handlereceptionattachmentpreview", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1217", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_handlereceptionattachmentdownload", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1222", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_views_quality_inspection_qc_work_order_index_vue", "target": "qc_work_order_index_taskattachmentfullurl", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/wuzilong/work/00code/zkxw/web-admin/src/views/quality-inspection/qc-work-order/index.vue", "source_location": "L1226", "weight": 1.0}, {"source": "users_wuzilong_work_00code_zkxw_web_admin_src_vieLine truncated
|
||||
@@ -10,21 +10,42 @@ import {
|
||||
qcStatusLabel,
|
||||
qcStatusTagType,
|
||||
scheduleStatusLabel,
|
||||
scheduleStatusTagType
|
||||
scheduleStatusTagType,
|
||||
sexLabel
|
||||
} from '@/utils/status-tag-registry'
|
||||
|
||||
/** 出库质检 + 中试服务:申请单 / 患者 / 输注方案 / 输注计划 / 产品明细 */
|
||||
export function buildOutboundPilotContextSections(detail: OutboundQcDetail): ViewSection[] {
|
||||
export type OutboundViewContextVariant = 'pilot_service' | 'clinical_application'
|
||||
|
||||
const SECTION_TITLES: Record<
|
||||
OutboundViewContextVariant,
|
||||
{ application: string; schedule: string }
|
||||
> = {
|
||||
pilot_service: {
|
||||
application: '中试服务申请单信息',
|
||||
schedule: '本次输注计划信息'
|
||||
},
|
||||
clinical_application: {
|
||||
application: '临床应用申请信息',
|
||||
schedule: '临床应用输注计划信息'
|
||||
}
|
||||
}
|
||||
|
||||
/** 出库工单/质检:申请单 / 患者 / 输注方案 / 输注计划 / 产品明细(码值转中文标签) */
|
||||
export function buildOutboundPilotContextSections(
|
||||
detail: OutboundQcDetail,
|
||||
variant: OutboundViewContextVariant = 'pilot_service'
|
||||
): ViewSection[] {
|
||||
const app = detail.application
|
||||
const patient = detail.patient
|
||||
const plan = detail.infusionPlan
|
||||
const schedule = detail.infusionSchedule
|
||||
const products = detail.products || []
|
||||
const titles = SECTION_TITLES[variant]
|
||||
const result: ViewSection[] = []
|
||||
|
||||
if (app) {
|
||||
result.push({
|
||||
title: '中试服务申请单信息',
|
||||
title: titles.application,
|
||||
fields: [
|
||||
{ label: '申请单号', value: app.applicationNo || '—' },
|
||||
{ label: '申请单名称', value: app.applicationName || '—' },
|
||||
@@ -43,7 +64,7 @@ export function buildOutboundPilotContextSections(detail: OutboundQcDetail): Vie
|
||||
title: '患者信息',
|
||||
fields: [
|
||||
{ label: '患者姓名', value: patient.patientName || '—' },
|
||||
{ label: '性别', value: patient.sex || '—' },
|
||||
{ label: '性别', value: sexLabel(String(patient.sex ?? '')) || '—' },
|
||||
{ label: '年龄', value: patient.age || '—' },
|
||||
{ label: '联系电话', value: patient.phoneNo || '—' },
|
||||
{
|
||||
@@ -98,7 +119,7 @@ export function buildOutboundPilotContextSections(detail: OutboundQcDetail): Vie
|
||||
if (schedule.cancelReason) {
|
||||
scheduleFields.push({ label: '取消原因', value: schedule.cancelReason, span: true })
|
||||
}
|
||||
result.push({ title: '本次输注计划信息', fields: scheduleFields })
|
||||
result.push({ title: titles.schedule, fields: scheduleFields })
|
||||
}
|
||||
|
||||
if (products.length) {
|
||||
|
||||
+103
-15
@@ -212,31 +212,58 @@
|
||||
v-for="(att, attIdx) in row.attachments"
|
||||
:key="attIdx"
|
||||
class="paper-att-card"
|
||||
:class="{
|
||||
'paper-att-card--image': isImageFile(att),
|
||||
'paper-att-card--video': isVideoFile(att)
|
||||
}"
|
||||
:title="att.fileName"
|
||||
>
|
||||
<el-icon class="paper-att-card__icon"><Document /></el-icon>
|
||||
<span class="paper-att-card__name" :title="att.fileName">{{
|
||||
att.fileName || '附件'
|
||||
}}</span>
|
||||
<div
|
||||
class="paper-att-card__preview"
|
||||
:class="{
|
||||
'is-clickable': isImageFile(att) || isVideoFile(att)
|
||||
}"
|
||||
@click="
|
||||
(isImageFile(att) || isVideoFile(att)) && handlePreviewPaperAttachment(att)
|
||||
"
|
||||
>
|
||||
<img
|
||||
v-if="isImageFile(att)"
|
||||
:src="fullFileUrl(att.attachmentUrl)"
|
||||
:alt="att.fileName || '附件'"
|
||||
/>
|
||||
<template v-else-if="isVideoFile(att)">
|
||||
<div class="paper-att-card__type-icon paper-att-card__type-icon--video">
|
||||
<el-icon><VideoPlay /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-icon class="paper-att-card__type-icon paper-att-card__type-icon--doc">
|
||||
<Document />
|
||||
</el-icon>
|
||||
<span class="paper-att-card__name">{{ att.fileName || '附件' }}</span>
|
||||
</template>
|
||||
</div>
|
||||
<div class="paper-att-card__actions">
|
||||
<el-icon
|
||||
v-if="isImageFile(att)"
|
||||
v-if="isImageFile(att) || isVideoFile(att)"
|
||||
class="paper-att-card__action"
|
||||
title="预览"
|
||||
@click="handlePreviewPaperAttachment(att)"
|
||||
@click.stop="handlePreviewPaperAttachment(att)"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action"
|
||||
title="下载"
|
||||
@click="handleDownloadPaperAttachment(att)"
|
||||
@click.stop="handleDownloadPaperAttachment(att)"
|
||||
>
|
||||
<Download />
|
||||
</el-icon>
|
||||
<el-icon
|
||||
class="paper-att-card__action paper-att-card__action--danger"
|
||||
title="删除"
|
||||
@click="removePaperAttachment($index, attIdx)"
|
||||
@click.stop="removePaperAttachment($index, attIdx)"
|
||||
>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
@@ -398,7 +425,8 @@
|
||||
Search,
|
||||
Document,
|
||||
Download,
|
||||
View
|
||||
View,
|
||||
VideoPlay
|
||||
} from '@element-plus/icons-vue'
|
||||
import { fetchUploadAttachment } from '@/api/business'
|
||||
import { fetchCellProductList } from '@/api/cell-product'
|
||||
@@ -908,6 +936,11 @@
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
:deep(.el-upload) {
|
||||
display: block;
|
||||
line-height: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card {
|
||||
@@ -918,17 +951,62 @@
|
||||
background: #f8fafc;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
align-items: stretch;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.paper-att-card__icon {
|
||||
color: #0284c7;
|
||||
font-size: 18px;
|
||||
.paper-att-card__preview {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 4px;
|
||||
overflow: hidden;
|
||||
|
||||
&.is-clickable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card--image,
|
||||
.paper-att-card--video {
|
||||
.paper-att-card__preview {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card__type-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&--doc {
|
||||
color: #0284c7;
|
||||
}
|
||||
|
||||
&--video {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background: #ede9fe;
|
||||
color: #7c3aed;
|
||||
font-size: 20px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.paper-att-card__name {
|
||||
@@ -978,6 +1056,10 @@
|
||||
}
|
||||
|
||||
.paper-att-card--trigger {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
padding: 6px 4px;
|
||||
border: 1px dashed #a7f3d0;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
@@ -995,14 +1077,20 @@
|
||||
}
|
||||
|
||||
.paper-att-card__trigger-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
color: #10b981;
|
||||
margin-bottom: 2px;
|
||||
margin: 0;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.paper-att-card--trigger span {
|
||||
font-size: 11px;
|
||||
color: #64748b;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preview-container {
|
||||
|
||||
@@ -33,9 +33,22 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="输注时间">
|
||||
<el-date-picker
|
||||
v-model="searchForm.infusionTimeRange"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="filter-actions-row">
|
||||
<span class="filter-hint">支持按申请单号、患者姓名搜索。</span>
|
||||
<span class="filter-hint"
|
||||
>支持按申请单号、患者姓名搜索;输注时间默认今天前 15 天至后 15 天。</span
|
||||
>
|
||||
<div class="filter-actions-end">
|
||||
<el-button class="btn-ghost" @click="handleReset">
|
||||
<el-icon><RefreshLeft /></el-icon>重置
|
||||
@@ -267,9 +280,27 @@
|
||||
{ value: 'btg', label: '不通过' }
|
||||
]
|
||||
|
||||
function formatLocalDate(d: Date) {
|
||||
const y = d.getFullYear()
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(d.getDate()).padStart(2, '0')
|
||||
return `${y}-${m}-${day}`
|
||||
}
|
||||
|
||||
/** 输注时间默认:今天前 15 天 ~ 今天后 15 天 */
|
||||
function getDefaultInfusionTimeRange(): [string, string] {
|
||||
const today = new Date()
|
||||
const start = new Date(today)
|
||||
start.setDate(start.getDate() - 15)
|
||||
const end = new Date(today)
|
||||
end.setDate(end.getDate() + 15)
|
||||
return [formatLocalDate(start), formatLocalDate(end)]
|
||||
}
|
||||
|
||||
const searchForm = reactive({
|
||||
reviewConclusion: '',
|
||||
keyword: ''
|
||||
keyword: '',
|
||||
infusionTimeRange: getDefaultInfusionTimeRange() as [string, string]
|
||||
})
|
||||
const tableData = ref<OutboundReleaseReview[]>([])
|
||||
const selectedRows = ref<OutboundReleaseReview[]>([])
|
||||
@@ -290,7 +321,9 @@
|
||||
pageSize: pagination.pageSize,
|
||||
reviewConclusion: searchForm.reviewConclusion || undefined,
|
||||
keyword: searchForm.keyword || undefined,
|
||||
bizType: effectiveBizType.value
|
||||
bizType: effectiveBizType.value,
|
||||
infusionTimeStart: searchForm.infusionTimeRange?.[0] || undefined,
|
||||
infusionTimeEnd: searchForm.infusionTimeRange?.[1] || undefined
|
||||
})
|
||||
tableData.value = res.list || []
|
||||
pagination.total = res.total ?? 0
|
||||
@@ -307,6 +340,7 @@
|
||||
function handleReset() {
|
||||
searchForm.reviewConclusion = ''
|
||||
searchForm.keyword = ''
|
||||
searchForm.infusionTimeRange = getDefaultInfusionTimeRange()
|
||||
pagination.page = 1
|
||||
loadData()
|
||||
}
|
||||
|
||||
@@ -199,10 +199,10 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
{{
|
||||
row.amount
|
||||
row.amount != null && row.amount !== ''
|
||||
? '¥' +
|
||||
Number(row.amount).toLocaleString('zh-CN', { minimumFractionDigits: 2 })
|
||||
: '-'
|
||||
: '—'
|
||||
}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -215,13 +215,13 @@
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="row.paymentStatus"
|
||||
:type="payStatusTagType(row.paymentStatus)"
|
||||
v-if="row.payStatus"
|
||||
:type="payStatusTagType(row.payStatus)"
|
||||
size="small"
|
||||
>
|
||||
{{ payStatusLabel(row.paymentStatus) }}
|
||||
{{ payStatusLabel(row.payStatus) }}
|
||||
</el-tag>
|
||||
<span v-else>-</span>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -552,10 +552,21 @@
|
||||
}
|
||||
|
||||
const buildCompleteConfirmMessage = (row: any) => {
|
||||
const orderTypeLabel = channelServiceOrderTypeLabel(row.orderType)
|
||||
const patient = row.patientName ? `患者「${row.patientName}」` : '该患者'
|
||||
const pendingConfirmHint =
|
||||
getInfusionStatus(row) === 'dtz' ? '当前为待输注,系统将先记录用户确认再标记完成。' : ''
|
||||
|
||||
if (isClinical.value) {
|
||||
const paymentHint =
|
||||
'临床应用不在此扣减渠道服务订单费用,缴费请在「缴费信息」中单独处理。'
|
||||
return `${patient}的输注计划将标记为已完成。${pendingConfirmHint}${paymentHint}是否继续?`
|
||||
}
|
||||
|
||||
const orderTypeLabel = channelServiceOrderTypeLabel(row.orderType)
|
||||
const statusHint =
|
||||
getInfusionStatus(row) === 'dtz' ? '当前为待输注,系统将先记录用户确认再完成结算。' : ''
|
||||
getInfusionStatus(row) === 'dtz'
|
||||
? '当前为待输注,系统将先记录用户确认再完成结算。'
|
||||
: ''
|
||||
let settlementHint = '并触发关联服务订单的结算扣减。'
|
||||
if (row.orderType === 'yfk') {
|
||||
settlementHint = `关联服务订单类型为「${orderTypeLabel}」,系统将按渠道协议单价扣减服务订单账户余额。`
|
||||
@@ -603,7 +614,9 @@
|
||||
scheduleId,
|
||||
infusionStatus: 'ywc'
|
||||
})
|
||||
ElMessage.success('输注计划已完成,结算扣减已处理')
|
||||
ElMessage.success(
|
||||
isClinical.value ? '输注计划已完成' : '输注计划已完成,结算扣减已处理'
|
||||
)
|
||||
loadData()
|
||||
} catch (e: any) {
|
||||
if (e !== 'cancel') {
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
</el-tag>
|
||||
<el-button
|
||||
v-if="editor.selectedStep.value.stepStatus === 'dzx'"
|
||||
class="btn-op-item primary"
|
||||
size="small"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="primary"
|
||||
:loading="editor.stepActionLoading.value"
|
||||
:disabled="editor.manualSaveLoading.value"
|
||||
@click="editor.handleStartStep(editor.selectedStep.value)"
|
||||
@@ -35,9 +35,8 @@
|
||||
</el-button>
|
||||
</template>
|
||||
<el-button
|
||||
class="btn-main"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.manualSaveLoading.value"
|
||||
:disabled="editor.stepActionLoading.value"
|
||||
@click="editor.handleManualSave"
|
||||
|
||||
@@ -40,9 +40,8 @@
|
||||
<div v-if="!readonly" class="execution-header-actions">
|
||||
<el-button
|
||||
v-if="editor.taskDetail.value.taskStatus === 'dpz'"
|
||||
class="btn-main"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.taskActionLoading.value"
|
||||
@click="editor.handleStartTask"
|
||||
>
|
||||
@@ -53,9 +52,8 @@
|
||||
editor.taskDetail.value.taskStatus === 'jxz' ||
|
||||
editor.taskDetail.value.taskStatus === 'ywc'
|
||||
"
|
||||
class="btn-main"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="success"
|
||||
size="small"
|
||||
:loading="editor.taskActionLoading.value"
|
||||
@click="editor.handleCompleteTask"
|
||||
>
|
||||
@@ -263,13 +261,13 @@
|
||||
<h3>📥 原材料(样本/细胞产品)<span class="virtual-step-required">必填</span></h3>
|
||||
<div v-if="!readonly" class="execution-detail-actions">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.inputSaving.value"
|
||||
@click="editor.saveTaskInputs"
|
||||
><el-icon><Check /></el-icon>保存</el-button
|
||||
>
|
||||
<el-icon><Check /></el-icon>保存
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="execution-detail-body">
|
||||
@@ -293,13 +291,13 @@
|
||||
<h3>📤 产出(细胞产品)</h3>
|
||||
<div v-if="!readonly" class="execution-detail-actions">
|
||||
<el-button
|
||||
class="btn-main"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="primary"
|
||||
size="small"
|
||||
:loading="editor.outputSaving.value"
|
||||
@click="editor.saveTaskOutputs"
|
||||
><el-icon><Check /></el-icon>保存</el-button
|
||||
>
|
||||
<el-icon><Check /></el-icon>保存
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="execution-detail-body">
|
||||
@@ -712,3 +710,26 @@
|
||||
margin-right: 8px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 弹窗 teleport 到 body,工具栏按钮样式需挂 execution-dialog 全局类 -->
|
||||
<style lang="scss">
|
||||
.execution-dialog {
|
||||
.task-exec-toolbar-btn.el-button {
|
||||
height: 36px;
|
||||
min-height: 36px;
|
||||
padding: 0 14px;
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
> span {
|
||||
font-size: 14px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.el-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,11 +4,11 @@
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">原材料(样本/细胞产品)</h2>
|
||||
<div v-if="!disabled" class="panel-top-actions">
|
||||
<el-button type="primary" size="small" @click="handleAddSample">
|
||||
<el-button class="btn-main task-exec-toolbar-btn" type="primary" @click="handleAddSample">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加样本
|
||||
</el-button>
|
||||
<el-button size="small" @click="handleAddCellProduct">
|
||||
<el-button class="btn-ghost task-exec-toolbar-btn" @click="handleAddCellProduct">
|
||||
<el-icon><Plus /></el-icon>
|
||||
添加细胞产品
|
||||
</el-button>
|
||||
|
||||
@@ -3,7 +3,12 @@
|
||||
<!-- 工具栏 -->
|
||||
<div class="panel-top">
|
||||
<h2 class="title-bar-heading">产出(细胞产品)</h2>
|
||||
<el-button v-if="!disabled" type="primary" size="small" @click="handleAdd">
|
||||
<el-button
|
||||
v-if="!disabled"
|
||||
class="btn-main task-exec-toolbar-btn"
|
||||
type="primary"
|
||||
@click="handleAdd"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
新增产出
|
||||
</el-button>
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
<!-- 生产工单展开行 · 生产任务子列表 -->
|
||||
<template>
|
||||
<div class="work-order-task-sublist">
|
||||
<div class="list-toolbar work-order-task-sublist__toolbar">
|
||||
<div class="list-toolbar-row">
|
||||
<div class="work-order-task-sublist__panel" v-loading="loading">
|
||||
<div v-if="tableData.length" class="work-order-task-cards-wrap">
|
||||
<div class="work-order-task-sublist__summary">
|
||||
<span class="work-order-task-sublist__count">共 {{ tableData.length }} 项任务</span>
|
||||
<el-button
|
||||
class="btn-main"
|
||||
class="work-order-task-sublist__refresh-icon"
|
||||
link
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="loadData"
|
||||
>
|
||||
<el-icon><RefreshLeft /></el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-main work-order-task-sublist__dispatch"
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!canDispatch"
|
||||
@@ -12,61 +23,24 @@
|
||||
>
|
||||
<el-icon><Position /></el-icon>派发任务
|
||||
</el-button>
|
||||
<el-button class="btn-ghost" size="small" :loading="loading" @click="loadData">
|
||||
<el-icon><RefreshLeft /></el-icon>刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="work-order-task-sublist__body">
|
||||
<el-table
|
||||
class="list-table work-order-task-sublist__table"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
size="small"
|
||||
@row-dblclick="handleViewDetail"
|
||||
<div
|
||||
v-for="(row, idx) in tableData"
|
||||
:key="row.id || idx"
|
||||
class="work-order-task-card"
|
||||
@dblclick="handleViewDetail(row)"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
:index="(i) => i + 1"
|
||||
/>
|
||||
<el-table-column prop="taskNo" label="任务号" min-width="150" align="center" />
|
||||
<el-table-column prop="taskType" label="任务类型" min-width="120" align="center">
|
||||
<template #default="{ row }">
|
||||
<div class="work-order-task-card__head">
|
||||
<span class="work-order-task-card__name">{{ row.taskName || row.taskNo || '—' }}</span>
|
||||
<el-tag :type="taskStatusTag(row.taskStatus).type" size="small">
|
||||
{{ taskStatusTag(row.taskStatus).label }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="work-order-task-card__meta">
|
||||
<span class="work-order-task-card__code">{{ row.taskNo || '—' }}</span>
|
||||
<el-tag :type="productionTaskTypeTag(row.taskType, row.orderSource).type" size="small">
|
||||
{{ productionTaskTypeTag(row.taskType, row.orderSource).label }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="taskName"
|
||||
label="任务名称"
|
||||
min-width="160"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
prop="routeName"
|
||||
label="工艺路线"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
>
|
||||
<template #default="{ row }">{{ row.routeName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="taskStatus" label="任务状态" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="taskStatusTag(row.taskStatus).type" size="small">{{
|
||||
taskStatusTag(row.taskStatus).label
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="conclusion" label="生产结论" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
v-if="productionConclusionTagType(row.conclusion)"
|
||||
:type="productionConclusionTagType(row.conclusion)"
|
||||
@@ -74,27 +48,24 @@
|
||||
>
|
||||
{{ row.conclusionLabel || productionConclusionLabel(row.conclusion) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startedTime" label="开始时间" min-width="170" align="center">
|
||||
<template #default="{ row }">{{ row.startedTime || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="completedTime" label="完成时间" min-width="170" align="center">
|
||||
<template #default="{ row }">{{ row.completedTime || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="260" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
</div>
|
||||
<div class="work-order-task-sublist__ops">
|
||||
<el-button
|
||||
v-if="canEnterExecution(row)"
|
||||
class="btn-op-item primary"
|
||||
type="primary"
|
||||
@click="handleEnterExecution(row)"
|
||||
size="small"
|
||||
@click.stop="handleEnterExecution(row)"
|
||||
>
|
||||
<el-icon><VideoPlay /></el-icon>进入执行
|
||||
</el-button>
|
||||
<el-button v-else class="btn-op-item primary" @click="handleViewDetail(row)">
|
||||
<el-button
|
||||
v-else
|
||||
class="btn-op-item primary"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="handleViewDetail(row)"
|
||||
>
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
<el-dropdown
|
||||
@@ -102,7 +73,7 @@
|
||||
trigger="hover"
|
||||
@command="(cmd) => handleDropdownCommand(cmd, row)"
|
||||
>
|
||||
<el-button class="btn-op-item">
|
||||
<el-button class="btn-op-item" size="small" @click.stop>
|
||||
<el-icon><More /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
@@ -123,15 +94,36 @@
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!loading" class="work-order-task-cards-wrap work-order-task-cards-wrap--empty">
|
||||
<div class="work-order-task-sublist__summary">
|
||||
<el-button
|
||||
class="work-order-task-sublist__refresh-icon"
|
||||
link
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="loadData"
|
||||
>
|
||||
<el-icon><RefreshLeft /></el-icon>
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn-main work-order-task-sublist__dispatch"
|
||||
type="primary"
|
||||
size="small"
|
||||
:disabled="!canDispatch"
|
||||
@click="handleDispatch"
|
||||
>
|
||||
<el-icon><Position /></el-icon>派发任务
|
||||
</el-button>
|
||||
</div>
|
||||
<el-empty
|
||||
v-if="!loading && !tableData.length"
|
||||
class="work-order-task-sublist__empty"
|
||||
description="暂无任务,可点击「派发任务」创建"
|
||||
:image-size="72"
|
||||
:image-size="56"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TaskExecutionDialog
|
||||
v-model:visible="executionDialogVisible"
|
||||
@@ -325,36 +317,139 @@
|
||||
|
||||
<style scoped lang="scss">
|
||||
.work-order-task-sublist {
|
||||
padding: 12px 16px 16px 40px;
|
||||
padding: 8px 12px 10px 40px;
|
||||
background: #f8fafc;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__toolbar {
|
||||
padding: 0 0 8px;
|
||||
border-bottom: none;
|
||||
.work-order-task-sublist__panel {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__body {
|
||||
display: block;
|
||||
width: 100%;
|
||||
.work-order-task-cards-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__summary {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 4px 8px;
|
||||
padding-top: 8px;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__count {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__refresh-icon {
|
||||
flex-shrink: 0;
|
||||
padding: 0 2px;
|
||||
height: 20px;
|
||||
font-size: 14px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.work-order-task-sublist__dispatch {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.work-order-task-cards-wrap--empty {
|
||||
align-items: center;
|
||||
|
||||
.work-order-task-sublist__summary {
|
||||
padding-top: 0;
|
||||
min-height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.work-order-task-sublist__empty {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
padding: 4px 0;
|
||||
|
||||
:deep(.el-empty__description) {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.work-order-task-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
flex: 0 0 248px;
|
||||
width: 248px;
|
||||
max-width: 248px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
cursor: default;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #a7f3d0;
|
||||
box-shadow: 0 2px 6px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.work-order-task-card__head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.work-order-task-card__name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
line-height: 1.35;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.work-order-task-card__meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.work-order-task-card__code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 11px;
|
||||
color: #475569;
|
||||
line-height: 1.35;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__table {
|
||||
width: 100%;
|
||||
|
||||
:deep(.el-table__inner-wrapper),
|
||||
:deep(.el-table__header-wrapper),
|
||||
:deep(.el-table__body-wrapper) {
|
||||
width: 100%;
|
||||
}
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.work-order-task-sublist__ops {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -141,9 +141,14 @@
|
||||
align="center"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.bizType === 'pilot_service' ? 'primary' : 'success'" size="small">
|
||||
{{ row.bizTypeLabel || '—' }}
|
||||
<el-tag
|
||||
v-if="row.bizType"
|
||||
:type="bizTypeTagType(row.bizType) as TagType"
|
||||
size="small"
|
||||
>
|
||||
{{ row.bizTypeLabel || bizTypeLabel(row.bizType) }}
|
||||
</el-tag>
|
||||
<span v-else>—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -546,7 +551,9 @@
|
||||
donationStatusLabel,
|
||||
donationStatusTagType,
|
||||
productStatusLabel,
|
||||
productStatusTagType
|
||||
productStatusTagType,
|
||||
bizTypeLabel,
|
||||
bizTypeTagType
|
||||
} from '@/utils/status-tag-registry'
|
||||
import { buildOutboundPilotContextSections } from '@/utils/outbound-qc-pilot-view-sections'
|
||||
import type { OutboundQcDetail } from '@/api/qc-work-order'
|
||||
@@ -873,15 +880,17 @@
|
||||
|
||||
const viewVisible = ref(false)
|
||||
const detail = ref<ProductionWorkOrderDetail | null>(null)
|
||||
const isOutboundPilotService = computed(
|
||||
const isOutboundContextView = computed(
|
||||
() =>
|
||||
detail.value?.workOrderType === 'outbound' && detail.value?.bizType === 'pilot_service'
|
||||
detail.value?.workOrderType === 'outbound' &&
|
||||
(detail.value?.bizType === 'pilot_service' ||
|
||||
detail.value?.bizType === 'clinical_application')
|
||||
)
|
||||
const viewDialogWidth = computed(() => (isOutboundPilotService.value ? '1200px' : '720px'))
|
||||
const viewDialogWidth = computed(() => (isOutboundContextView.value ? '1200px' : '720px'))
|
||||
const viewDialogGridColumns = computed<2 | 3 | 4>(() =>
|
||||
isOutboundPilotService.value ? 4 : 2
|
||||
isOutboundContextView.value ? 4 : 2
|
||||
)
|
||||
const viewDialogCompact = computed(() => isOutboundPilotService.value)
|
||||
const viewDialogCompact = computed(() => isOutboundContextView.value)
|
||||
const minioBaseUrl = (import.meta.env.VITE_MINIO_BASE_URL || '').replace(/\/$/, '')
|
||||
|
||||
const viewReceptionAttachmentFiles = computed(() => {
|
||||
@@ -959,14 +968,8 @@
|
||||
if (isOutbound) {
|
||||
baseFields.push({
|
||||
label: '业务类型',
|
||||
value:
|
||||
d.bizTypeLabel ||
|
||||
(d.bizType === 'pilot_service'
|
||||
? '中试服务'
|
||||
: d.bizType === 'clinical_application'
|
||||
? '临床应用'
|
||||
: '—'),
|
||||
tagType: 'primary' as const
|
||||
value: d.bizTypeLabel || bizTypeLabel(d.bizType || '') || '—',
|
||||
tagType: bizTypeTagType(d.bizType || '') as TagType
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1010,10 +1013,9 @@
|
||||
|
||||
const sections: ViewSection[] = [{ title: '基本信息', fields: baseFields }]
|
||||
|
||||
// outbound: 中试服务 — 与出库质检工单查看弹窗一致;临床应用 — 保留简要申请/输注信息
|
||||
if (isOutbound) {
|
||||
if (d.bizType === 'pilot_service') {
|
||||
const pilotDetail: OutboundQcDetail = {
|
||||
// outbound:中试服务 / 临床应用 — 与出库质检一致,码值展示中文标签
|
||||
if (isOutbound && (d.bizType === 'pilot_service' || d.bizType === 'clinical_application')) {
|
||||
const outboundDetail: OutboundQcDetail = {
|
||||
workOrder: {} as OutboundQcDetail['workOrder'],
|
||||
application: d.application,
|
||||
patient: d.patient,
|
||||
@@ -1021,51 +1023,9 @@
|
||||
infusionSchedule: d.infusionSchedule,
|
||||
products: d.products
|
||||
}
|
||||
sections.push(...buildOutboundPilotContextSections(pilotDetail))
|
||||
} else {
|
||||
const pa = d.pilotApplication || {}
|
||||
const sched = d.infusionSchedule || {}
|
||||
const appStatusLabel = (s: string) => {
|
||||
const m: Record<string, string> = {
|
||||
dks: '待开始',
|
||||
jxz: '进行中',
|
||||
ywc: '已完成',
|
||||
yzf: '已作废'
|
||||
}
|
||||
return m[s] || s || '—'
|
||||
}
|
||||
const appStatusTagType = (s: string) => {
|
||||
const m: Record<string, string> = {
|
||||
dks: 'info',
|
||||
jxz: 'warning',
|
||||
ywc: 'success',
|
||||
yzf: 'danger'
|
||||
}
|
||||
return m[s] || 'info'
|
||||
}
|
||||
sections.push({
|
||||
title: '中试申请信息',
|
||||
fields: [
|
||||
{ label: '申请单号', value: pa.applicationNo || '—' },
|
||||
{ label: '申请名称', value: pa.applicationName || '—' },
|
||||
{
|
||||
label: '申请状态',
|
||||
value: appStatusLabel(pa.applicationStatus || ''),
|
||||
tagType: appStatusTagType(pa.applicationStatus || '') as any
|
||||
}
|
||||
]
|
||||
})
|
||||
sections.push({
|
||||
title: '输注计划信息',
|
||||
fields: [
|
||||
{ label: '细胞类型', value: sched.cellType || '—' },
|
||||
{ label: '规格', value: sched.specText || '—' },
|
||||
{ label: '输注时间', value: sched.infusionTime || '—' },
|
||||
{ label: '输注地点', value: sched.infusionLocation || '—' },
|
||||
{ label: '输注方式', value: sched.infusionMethod || '—' }
|
||||
]
|
||||
})
|
||||
}
|
||||
const variant =
|
||||
d.bizType === 'clinical_application' ? 'clinical_application' : 'pilot_service'
|
||||
sections.push(...buildOutboundPilotContextSections(outboundDetail, variant))
|
||||
return sections
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,13 @@
|
||||
/>
|
||||
</template>
|
||||
<template #product-table>
|
||||
<el-table :data="detail?.products || []" stripe size="small" class="outbound-product-table">
|
||||
<div v-if="detail?.products?.length" class="view-subtable-wrap">
|
||||
<el-table
|
||||
:data="detail?.products || []"
|
||||
stripe
|
||||
size="small"
|
||||
class="list-table view-subtable outbound-product-table"
|
||||
>
|
||||
<el-table-column type="index" label="序号" min-width="60" align="center" />
|
||||
<el-table-column prop="projectCode" label="项目编码" min-width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="projectName" label="项目名称" min-width="120" align="center" show-overflow-tooltip />
|
||||
@@ -35,6 +41,8 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<p v-else class="view-empty-hint">暂无产品明细</p>
|
||||
</template>
|
||||
<template v-for="task in tasksWithAttachments" #[taskAttachmentSlotName(task.id)]>
|
||||
<div :key="task.id" class="outbound-qc-task-attachments">
|
||||
@@ -469,6 +477,24 @@
|
||||
}
|
||||
|
||||
.outbound-product-table {
|
||||
margin-top: 4px;
|
||||
margin-top: 0;
|
||||
|
||||
:deep(.el-table__header th.el-table__cell),
|
||||
:deep(.el-table__body td.el-table__cell) {
|
||||
font-size: 12px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
:deep(.el-table__header th.el-table__cell .cell),
|
||||
:deep(.el-table__body td.el-table__cell .cell) {
|
||||
font-size: 12px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
:deep(.el-tag) {
|
||||
--el-tag-font-size: 11px;
|
||||
height: 20px;
|
||||
padding: 0 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -2,87 +2,53 @@
|
||||
<!-- eslint-disable vue/no-v-model-argument -->
|
||||
<template>
|
||||
<div class="qc-work-order-task-sublist">
|
||||
<div class="list-toolbar qc-work-order-task-sublist__toolbar">
|
||||
<div class="list-toolbar-row">
|
||||
<el-button class="btn-ghost" size="small" :loading="loading" @click="loadData">
|
||||
<el-icon><RefreshLeft /></el-icon>刷新
|
||||
<div class="qc-work-order-task-sublist__panel" v-loading="loading">
|
||||
<div v-if="tableData.length" class="qc-task-cards-wrap">
|
||||
<div class="qc-work-order-task-sublist__summary">
|
||||
<span class="qc-work-order-task-sublist__count">共 {{ tableData.length }} 项任务</span>
|
||||
<el-button
|
||||
class="qc-work-order-task-sublist__refresh-icon"
|
||||
link
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="loadData"
|
||||
>
|
||||
<el-icon><RefreshLeft /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="qc-work-order-task-sublist__body sqxp-subtable-wrap">
|
||||
<el-table
|
||||
class="list-table qc-work-order-task-sublist__table sqxp-subtable"
|
||||
:data="tableData"
|
||||
v-loading="loading"
|
||||
stripe
|
||||
size="small"
|
||||
@row-dblclick="handleView"
|
||||
>
|
||||
<el-table-column
|
||||
type="index"
|
||||
label="序号"
|
||||
min-width="68"
|
||||
align="center"
|
||||
:index="(i) => i + 1"
|
||||
/>
|
||||
<el-table-column prop="taskNo" label="任务号" min-width="150" align="center" />
|
||||
<el-table-column prop="taskType" label="检测类型" min-width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.taskType === 'sysz' ? 'success' : 'info'" size="small">{{
|
||||
taskTypeLabel(row.taskType)
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="itemName" label="检测项目" min-width="160" align="center" />
|
||||
<el-table-column prop="companyName" label="第三方公司" min-width="120" align="center">
|
||||
<template #default="{ row }">{{ row.companyName || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="conclusion" label="质检结论" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.conclusion === 'tg'" type="success" size="small">
|
||||
{{ row.conclusionLabel || qcTaskConclusionLabel(row.conclusion) }}
|
||||
</el-tag>
|
||||
<el-tag v-else-if="row.conclusion === 'btg'" type="danger" size="small">
|
||||
{{ row.conclusionLabel || qcTaskConclusionLabel(row.conclusion) }}
|
||||
</el-tag>
|
||||
<el-tag v-else type="info" size="small">—</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="taskStatus" label="状态" min-width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag
|
||||
:type="
|
||||
row.taskStatus === 'ywc'
|
||||
? 'success'
|
||||
: row.taskStatus === 'jxz'
|
||||
? 'warning'
|
||||
: 'info'
|
||||
"
|
||||
size="small"
|
||||
<div
|
||||
v-for="(row, idx) in tableData"
|
||||
:key="row.id || idx"
|
||||
class="qc-task-card"
|
||||
@dblclick="handleView(row)"
|
||||
>
|
||||
<div class="qc-task-card__head">
|
||||
<span class="qc-task-card__name">{{ row.itemName || row.taskNo || '—' }}</span>
|
||||
<el-tag :type="taskStatusTagType(row.taskStatus)" size="small">
|
||||
{{ taskStatusLabel(row.taskStatus) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
prop="remark"
|
||||
label="备注"
|
||||
min-width="140"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
</div>
|
||||
<div class="qc-task-card__meta">
|
||||
<span class="qc-task-card__code">{{ row.taskNo || '—' }}</span>
|
||||
<el-tag :type="taskTypeTagType(row.taskType)" size="small">
|
||||
{{ taskTypeLabel(row.taskType) }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="row.conclusion === 'tg' || row.conclusion === 'btg'"
|
||||
:type="conclusionTagType(row.conclusion)"
|
||||
size="small"
|
||||
>
|
||||
<template #default="{ row }">{{ row.remark || '—' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" min-width="260" fixed="right" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ row.conclusionLabel || qcTaskConclusionLabel(row.conclusion) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<div class="qc-work-order-task-sublist__ops">
|
||||
<template v-if="hasPrimaryTaskActions(row)">
|
||||
<el-button
|
||||
v-if="canStartTask(row)"
|
||||
class="btn-op-item primary"
|
||||
type="primary"
|
||||
@click="handleStart(row)"
|
||||
size="small"
|
||||
@click.stop="handleStart(row)"
|
||||
>
|
||||
<el-icon><VideoPlay /></el-icon>开始
|
||||
</el-button>
|
||||
@@ -90,12 +56,19 @@
|
||||
v-if="canFillResult(row)"
|
||||
class="btn-op-item primary"
|
||||
type="primary"
|
||||
@click="openResult(row)"
|
||||
size="small"
|
||||
@click.stop="openResult(row)"
|
||||
>
|
||||
<el-icon><EditPen /></el-icon>填写结果
|
||||
</el-button>
|
||||
</template>
|
||||
<el-button v-else class="btn-op-item primary" @click="handleView(row)">
|
||||
<el-button
|
||||
v-else
|
||||
class="btn-op-item primary"
|
||||
type="primary"
|
||||
size="small"
|
||||
@click.stop="handleView(row)"
|
||||
>
|
||||
<el-icon><View /></el-icon>查看
|
||||
</el-button>
|
||||
<el-dropdown
|
||||
@@ -103,7 +76,7 @@
|
||||
trigger="hover"
|
||||
@command="(cmd) => handleDropdownCommand(cmd, row)"
|
||||
>
|
||||
<el-button class="btn-op-item">
|
||||
<el-button class="btn-op-item" size="small" @click.stop>
|
||||
<el-icon><More /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
@@ -118,15 +91,27 @@
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else-if="!loading" class="qc-task-cards-wrap qc-task-cards-wrap--empty">
|
||||
<div class="qc-work-order-task-sublist__summary">
|
||||
<el-button
|
||||
class="qc-work-order-task-sublist__refresh-icon"
|
||||
link
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
@click="loadData"
|
||||
>
|
||||
<el-icon><RefreshLeft /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<el-empty
|
||||
v-if="!loading && !tableData.length"
|
||||
class="qc-work-order-task-sublist__empty"
|
||||
description="暂无任务,请先在工单行「配置检测」"
|
||||
:image-size="72"
|
||||
:image-size="56"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog
|
||||
v-model="resultVisible"
|
||||
@@ -607,6 +592,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
function taskStatusTagType(status: string) {
|
||||
if (status === 'ywc') return 'success' as const
|
||||
if (status === 'jxz') return 'warning' as const
|
||||
return 'info' as const
|
||||
}
|
||||
|
||||
function taskTypeTagType(type: string) {
|
||||
return type === 'sysz' ? ('success' as const) : ('info' as const)
|
||||
}
|
||||
|
||||
function conclusionTagType(conclusion: string) {
|
||||
if (conclusion === 'tg') return 'success' as const
|
||||
if (conclusion === 'btg') return 'danger' as const
|
||||
return 'info' as const
|
||||
}
|
||||
|
||||
const resultSubmitLabel = computed(() =>
|
||||
resultTaskStatus.value === 'ywc' ? '保存' : '提交并完成'
|
||||
)
|
||||
@@ -1212,26 +1213,135 @@
|
||||
|
||||
<style scoped lang="scss">
|
||||
.qc-work-order-task-sublist {
|
||||
padding: 12px 16px 16px 40px;
|
||||
padding: 8px 12px 10px 40px;
|
||||
background: #f8fafc;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__toolbar {
|
||||
padding: 0 0 8px;
|
||||
border-bottom: none;
|
||||
.qc-work-order-task-sublist__panel {
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__body {
|
||||
display: block;
|
||||
width: 100%;
|
||||
.qc-task-cards-wrap {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__summary {
|
||||
flex: 0 0 auto;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
padding-top: 8px;
|
||||
min-height: 34px;
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__count {
|
||||
font-size: 12px;
|
||||
color: #94a3b8;
|
||||
line-height: 1.35;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__refresh-icon {
|
||||
flex-shrink: 0;
|
||||
padding: 0 2px;
|
||||
height: 20px;
|
||||
font-size: 14px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
|
||||
.qc-task-cards-wrap--empty {
|
||||
align-items: center;
|
||||
|
||||
.qc-work-order-task-sublist__summary {
|
||||
padding-top: 0;
|
||||
min-height: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__empty {
|
||||
flex: 1;
|
||||
min-width: 200px;
|
||||
padding: 4px 0;
|
||||
|
||||
:deep(.el-empty__description) {
|
||||
margin-top: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.qc-task-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
flex: 0 0 248px;
|
||||
width: 248px;
|
||||
max-width: 248px;
|
||||
padding: 8px 10px;
|
||||
border: 1px solid #e2e8f0;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
cursor: default;
|
||||
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
border-color: #a7f3d0;
|
||||
box-shadow: 0 2px 6px rgba(16, 185, 129, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.qc-task-card__head {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.qc-task-card__name {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
line-height: 1.35;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.qc-task-card__meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 4px 6px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.qc-task-card__code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
font-size: 11px;
|
||||
color: #475569;
|
||||
line-height: 1.35;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.qc-work-order-task-sublist__ops {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.form-section {
|
||||
|
||||
Reference in new issue
Block a user