药品流向管理内容优化-初步
This commit is contained in:
@@ -47,13 +47,10 @@
|
||||
<el-menu-item index="/fees/contracts">{{ TEXT.menu.feeContracts }}</el-menu-item>
|
||||
<el-menu-item index="/fees/special">{{ TEXT.menu.feeSpecials }}</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-sub-menu index="drug">
|
||||
<template #title>
|
||||
<el-icon><Box /></el-icon>
|
||||
<span>{{ TEXT.menu.drug }}</span>
|
||||
</template>
|
||||
<el-menu-item index="/drug/shipments">{{ TEXT.menu.drugShipments }}</el-menu-item>
|
||||
</el-sub-menu>
|
||||
<el-menu-item index="/drug/shipments">
|
||||
<el-icon><Box /></el-icon>
|
||||
<span>{{ TEXT.menu.drugShipments }}</span>
|
||||
</el-menu-item>
|
||||
<el-menu-item index="/file-versions">
|
||||
<el-icon><Files /></el-icon>
|
||||
<span>{{ TEXT.menu.fileVersionManagement }}</span>
|
||||
|
||||
@@ -151,8 +151,11 @@ export const TEXT = {
|
||||
trainedDate: "培训日期",
|
||||
authorizedDate: "授权日期",
|
||||
direction: "类型",
|
||||
trackingNo: "运单号",
|
||||
shipDate: "日期",
|
||||
trackingNo: "单号",
|
||||
shipDate: "寄出/回收日期",
|
||||
receiveDate: "接收日期",
|
||||
quantity: "数量",
|
||||
batchNo: "批号",
|
||||
drugDesc: "物品描述",
|
||||
carrier: "快递公司",
|
||||
fromParty: "发出方",
|
||||
@@ -245,7 +248,7 @@ export const TEXT = {
|
||||
feeContracts: "合同管理",
|
||||
feeSpecials: "特殊费用管理",
|
||||
drug: "药品管理",
|
||||
drugShipments: "运输/流向",
|
||||
drugShipments: "药品流向管理",
|
||||
fileVersionManagement: "文件版本管理",
|
||||
startupFeasibilityEthics: "立项与伦理",
|
||||
startupMeetingAuth: "启动与授权",
|
||||
@@ -419,7 +422,7 @@ export const TEXT = {
|
||||
uploadHint: "保存后可上传凭证/发票/其他附件",
|
||||
},
|
||||
drugShipments: {
|
||||
title: "药品运输/流向",
|
||||
title: "药品流向管理",
|
||||
subtitle: "登记寄送与回收信息",
|
||||
recordLabel: "运输记录",
|
||||
empty: "暂无运输记录",
|
||||
@@ -429,7 +432,7 @@ export const TEXT = {
|
||||
formSubtitle: "记录寄送/回收流向",
|
||||
uploadHint: "保存后可上传交接材料",
|
||||
detailTitle: "运输记录详情",
|
||||
detailSubtitle: "查看运输/流向信息与附件",
|
||||
detailSubtitle: "查看药品流向信息与附件",
|
||||
},
|
||||
fileVersionManagement: {
|
||||
title: "文件版本管理",
|
||||
|
||||
@@ -11,20 +11,38 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card v-loading="loading">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item :label="TEXT.common.fields.direction">{{ displayEnum(TEXT.enums.shipmentDirection, detail.direction) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.status">{{ displayEnum(TEXT.enums.shipmentStatus, detail.status) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.site">{{ detail.site_name || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.shipDate">{{ displayDate(detail.ship_date) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.carrier">{{ detail.carrier || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.trackingNo">{{ detail.tracking_no || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.fromParty">{{ detail.from_party || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.toParty">{{ detail.to_party || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.drugDesc" :span="2">{{ detail.drug_desc || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.remark" :span="2">{{ detail.remark || TEXT.common.fallback }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
<div class="content">
|
||||
<el-card>
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>{{ TEXT.common.labels.basicInfo }}</span>
|
||||
<el-tag :type="detail.status === 'PENDING' ? 'info' : detail.status === 'SIGNED' ? 'success' : 'primary'">
|
||||
{{ displayEnum(TEXT.enums.shipmentStatus, detail.status) }}
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item :label="TEXT.common.fields.site">{{ detail.site_name || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.direction">
|
||||
<el-tag :type="detail.direction === 'SEND' ? 'primary' : 'warning'" size="small">
|
||||
{{ displayEnum(TEXT.enums.shipmentDirection, detail.direction) }}
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
|
||||
<el-card :header="TEXT.modules.drugShipments.recordLabel">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item :label="TEXT.common.fields.trackingNo">{{ detail.tracking_no || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.carrier">{{ detail.carrier || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.shipDate">{{ displayDate(detail.ship_date) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.receiveDate">{{ displayDate(detail.receive_date) }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.quantity">{{ detail.quantity ?? TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.batchNo">{{ detail.batch_no || TEXT.common.fallback }}</el-descriptions-item>
|
||||
<el-descriptions-item :label="TEXT.common.fields.remark" :span="2">{{ detail.remark || TEXT.common.fallback }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<AttachmentList
|
||||
v-if="shipmentId"
|
||||
@@ -57,11 +75,11 @@ const detail = reactive<any>({
|
||||
status: "",
|
||||
site_name: "",
|
||||
ship_date: "",
|
||||
receive_date: "",
|
||||
quantity: null,
|
||||
batch_no: "",
|
||||
carrier: "",
|
||||
tracking_no: "",
|
||||
from_party: "",
|
||||
to_party: "",
|
||||
drug_desc: "",
|
||||
remark: "",
|
||||
});
|
||||
|
||||
@@ -109,6 +127,18 @@ onMounted(load);
|
||||
color: var(--ctms-text-secondary);
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
|
||||
@@ -9,49 +9,85 @@
|
||||
</div>
|
||||
|
||||
<el-card>
|
||||
<el-form :model="form" label-width="120px">
|
||||
<el-form-item :label="TEXT.common.fields.direction" required>
|
||||
<el-select v-model="form.direction" :placeholder="TEXT.common.placeholders.select">
|
||||
<el-option :label="TEXT.enums.shipmentDirection.SEND" value="SEND" />
|
||||
<el-option :label="TEXT.enums.shipmentDirection.RETURN" value="RETURN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.site" required>
|
||||
<el-input v-model="form.site_name" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.site" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.drugDesc" required>
|
||||
<el-input v-model="form.drug_desc" type="textarea" :rows="3" :placeholder="TEXT.common.placeholders.input" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.shipDate">
|
||||
<el-date-picker v-model="form.ship_date" type="date" value-format="YYYY-MM-DD" :placeholder="TEXT.common.placeholders.select" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.carrier">
|
||||
<el-input v-model="form.carrier" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.carrier" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.trackingNo">
|
||||
<el-input v-model="form.tracking_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.trackingNo" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.fromParty">
|
||||
<el-input v-model="form.from_party" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.fromParty" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.toParty">
|
||||
<el-input v-model="form.to_party" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.toParty" />
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.status" required>
|
||||
<el-select v-model="form.status" :placeholder="TEXT.common.placeholders.select">
|
||||
<el-option :label="TEXT.enums.shipmentStatus.PENDING" value="PENDING" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.IN_TRANSIT" value="IN_TRANSIT" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.SIGNED" value="SIGNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.RETURNED" value="RETURNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.EXCEPTION" value="EXCEPTION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.remark">
|
||||
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px" label-position="top">
|
||||
<h3 class="section-title">{{ TEXT.common.labels.basicInfo }}</h3>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.site" prop="center_id">
|
||||
<el-select v-model="form.center_id" :placeholder="TEXT.common.placeholders.select" style="width: 100%">
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
:key="site.id"
|
||||
:label="site.name || TEXT.common.fallback"
|
||||
:value="site.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.direction" prop="direction">
|
||||
<el-select v-model="form.direction" :placeholder="TEXT.common.placeholders.select" style="width: 100%">
|
||||
<el-option :label="TEXT.enums.shipmentDirection.SEND" value="SEND" />
|
||||
<el-option :label="TEXT.enums.shipmentDirection.RETURN" value="RETURN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.status" prop="status">
|
||||
<el-select v-model="form.status" :placeholder="TEXT.common.placeholders.select" style="width: 100%">
|
||||
<el-option :label="TEXT.enums.shipmentStatus.PENDING" value="PENDING" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.IN_TRANSIT" value="IN_TRANSIT" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.SIGNED" value="SIGNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.RETURNED" value="RETURNED" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.EXCEPTION" value="EXCEPTION" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<h3 class="section-title">{{ TEXT.modules.drugShipments.recordLabel }}</h3>
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.shipDate" prop="ship_date">
|
||||
<el-date-picker v-model="form.ship_date" type="date" value-format="YYYY-MM-DD" :placeholder="TEXT.common.placeholders.select" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.receiveDate" prop="receive_date">
|
||||
<el-date-picker v-model="form.receive_date" type="date" value-format="YYYY-MM-DD" :placeholder="TEXT.common.placeholders.select" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.quantity" prop="quantity">
|
||||
<el-input-number v-model="form.quantity" :min="0" :controls="false" :placeholder="TEXT.common.placeholders.input" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.batchNo" prop="batch_no">
|
||||
<el-input v-model="form.batch_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.batchNo" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.carrier" prop="carrier">
|
||||
<el-input v-model="form.carrier" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.carrier" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item :label="TEXT.common.fields.trackingNo" prop="tracking_no">
|
||||
<el-input v-model="form.tracking_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.trackingNo" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-form-item :label="TEXT.common.fields.remark" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="3" :placeholder="TEXT.common.placeholders.input" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" :loading="saving" @click="submit">{{ TEXT.common.actions.save }}</el-button>
|
||||
<el-button @click="goBack">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
<div class="actions">
|
||||
<el-button type="primary" :loading="saving" @click="submit">{{ TEXT.common.actions.save }}</el-button>
|
||||
<el-button @click="goBack">{{ TEXT.common.actions.cancel }}</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
@@ -74,6 +110,7 @@ import { useRoute, useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useStudyStore } from "../../store/study";
|
||||
import { createDrugShipment, getDrugShipment, updateDrugShipment } from "../../api/drugShipments";
|
||||
import { fetchSites } from "../../api/sites";
|
||||
import AttachmentList from "../../components/attachments/AttachmentList.vue";
|
||||
import StateEmpty from "../../components/StateEmpty.vue";
|
||||
import { TEXT } from "../../locales";
|
||||
@@ -82,37 +119,63 @@ const route = useRoute();
|
||||
const router = useRouter();
|
||||
const study = useStudyStore();
|
||||
const saving = ref(false);
|
||||
const formRef = ref();
|
||||
const sites = ref<any[]>([]);
|
||||
|
||||
const shipmentId = computed(() => route.params.shipmentId as string | undefined);
|
||||
const isEdit = computed(() => !!shipmentId.value);
|
||||
const studyId = computed(() => study.currentStudy?.id || "");
|
||||
|
||||
const form = reactive({
|
||||
center_id: "",
|
||||
direction: "SEND",
|
||||
site_name: "",
|
||||
drug_desc: "",
|
||||
ship_date: "",
|
||||
receive_date: "",
|
||||
quantity: null as number | null,
|
||||
batch_no: "",
|
||||
carrier: "",
|
||||
tracking_no: "",
|
||||
from_party: "",
|
||||
to_party: "",
|
||||
status: "PENDING",
|
||||
remark: "",
|
||||
});
|
||||
|
||||
const rules = {
|
||||
center_id: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
direction: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
ship_date: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
receive_date: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
quantity: [{ required: true, type: "number", message: TEXT.common.messages.required, trigger: "change" }],
|
||||
batch_no: [{ required: true, message: TEXT.common.messages.required, trigger: "blur" }],
|
||||
carrier: [{ required: true, message: TEXT.common.messages.required, trigger: "blur" }],
|
||||
tracking_no: [{ required: true, message: TEXT.common.messages.required, trigger: "blur" }],
|
||||
status: [{ required: true, message: TEXT.common.messages.required, trigger: "change" }],
|
||||
remark: [{ required: true, message: TEXT.common.messages.required, trigger: "blur" }],
|
||||
};
|
||||
|
||||
const loadSites = async () => {
|
||||
const studyId = study.currentStudy?.id;
|
||||
if (!studyId) return;
|
||||
try {
|
||||
const { data } = await fetchSites(studyId, { limit: 500 });
|
||||
sites.value = Array.isArray(data) ? data : data.items || [];
|
||||
} catch {
|
||||
sites.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
if (!isEdit.value || !studyId.value || !shipmentId.value) return;
|
||||
try {
|
||||
const { data } = await getDrugShipment(studyId.value, shipmentId.value);
|
||||
Object.assign(form, {
|
||||
direction: data.direction || "SEND",
|
||||
site_name: data.site_name || "",
|
||||
drug_desc: data.drug_desc || "",
|
||||
center_id: data.center_id || "",
|
||||
ship_date: data.ship_date || "",
|
||||
receive_date: data.receive_date || "",
|
||||
quantity: data.quantity ?? null,
|
||||
batch_no: data.batch_no || "",
|
||||
carrier: data.carrier || "",
|
||||
tracking_no: data.tracking_no || "",
|
||||
from_party: data.from_party || "",
|
||||
to_party: data.to_party || "",
|
||||
status: data.status || "PENDING",
|
||||
remark: data.remark || "",
|
||||
});
|
||||
@@ -123,17 +186,19 @@ const load = async () => {
|
||||
|
||||
const submit = async () => {
|
||||
if (!studyId.value) return;
|
||||
const valid = await formRef.value?.validate().catch(() => false);
|
||||
if (!valid) return;
|
||||
saving.value = true;
|
||||
try {
|
||||
const payload = {
|
||||
center_id: form.center_id,
|
||||
direction: form.direction,
|
||||
site_name: form.site_name,
|
||||
drug_desc: form.drug_desc,
|
||||
ship_date: form.ship_date || null,
|
||||
receive_date: form.receive_date || null,
|
||||
quantity: form.quantity,
|
||||
batch_no: form.batch_no,
|
||||
carrier: form.carrier || null,
|
||||
tracking_no: form.tracking_no || null,
|
||||
from_party: form.from_party || null,
|
||||
to_party: form.to_party || null,
|
||||
status: form.status,
|
||||
remark: form.remark || null,
|
||||
};
|
||||
@@ -155,7 +220,10 @@ const submit = async () => {
|
||||
|
||||
const goBack = () => router.push("/drug/shipments");
|
||||
|
||||
onMounted(load);
|
||||
onMounted(async () => {
|
||||
await loadSites();
|
||||
load();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@@ -186,4 +254,18 @@ onMounted(load);
|
||||
.hint-card {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
margin: 0 0 24px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--ctms-text-primary);
|
||||
border-left: 4px solid var(--ctms-primary);
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -157,7 +157,7 @@
|
||||
{{ scope.row.remark || TEXT.common.fallback }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.common.actions.delete" width="120">
|
||||
<el-table-column :label="TEXT.common.labels.actions" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
<span v-else class="text-muted">-</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.common.actions.delete" width="120">
|
||||
<el-table-column :label="TEXT.common.labels.actions" width="120">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
|
||||
@@ -11,13 +11,23 @@
|
||||
<el-card class="filter-card">
|
||||
<el-form :inline="true" :model="filters">
|
||||
<el-form-item :label="TEXT.common.fields.site">
|
||||
<el-input v-model="filters.site_name" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.site" clearable />
|
||||
<el-select v-model="filters.center_id" :placeholder="TEXT.common.placeholders.select" clearable style="width: 200px">
|
||||
<el-option
|
||||
v-for="site in sites"
|
||||
:key="site.id"
|
||||
:label="site.name || TEXT.common.fallback"
|
||||
:value="site.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.trackingNo">
|
||||
<el-input v-model="filters.tracking_no" :placeholder="TEXT.common.placeholders.input + TEXT.common.fields.trackingNo" clearable />
|
||||
<el-form-item :label="TEXT.common.fields.direction">
|
||||
<el-select v-model="filters.direction" :placeholder="TEXT.common.placeholders.select" clearable style="width: 160px">
|
||||
<el-option :label="TEXT.enums.shipmentDirection.SEND" value="SEND" />
|
||||
<el-option :label="TEXT.enums.shipmentDirection.RETURN" value="RETURN" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label="TEXT.common.fields.status">
|
||||
<el-select v-model="filters.status" :placeholder="TEXT.common.placeholders.select" clearable>
|
||||
<el-select v-model="filters.status" :placeholder="TEXT.common.placeholders.select" clearable style="width: 160px">
|
||||
<el-option :label="TEXT.enums.shipmentStatus.PENDING" value="PENDING" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.IN_TRANSIT" value="IN_TRANSIT" />
|
||||
<el-option :label="TEXT.enums.shipmentStatus.SIGNED" value="SIGNED" />
|
||||
@@ -33,23 +43,42 @@
|
||||
</el-card>
|
||||
|
||||
<el-card>
|
||||
<el-table :data="items" v-loading="loading" style="width: 100%">
|
||||
<el-table-column prop="direction" :label="TEXT.common.fields.direction" width="100">
|
||||
<template #default="scope">{{ displayEnum(TEXT.enums.shipmentDirection, scope.row.direction) }}</template>
|
||||
<el-table :data="items" v-loading="loading" style="width: 100%" @row-click="onRowClick">
|
||||
<el-table-column prop="site_name" :label="TEXT.common.fields.site" min-width="160">
|
||||
<template #default="scope">{{ scope.row.site_name || TEXT.common.fallback }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="direction" :label="TEXT.common.fields.direction" width="100">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.direction === 'SEND' ? 'primary' : 'warning'" disable-transitions>
|
||||
{{ displayEnum(TEXT.enums.shipmentDirection, scope.row.direction) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="site_name" :label="TEXT.common.fields.site" min-width="160" />
|
||||
<el-table-column prop="tracking_no" :label="TEXT.common.fields.trackingNo" min-width="160" />
|
||||
<el-table-column prop="ship_date" :label="TEXT.common.fields.shipDate" width="140">
|
||||
<template #default="scope">{{ displayDate(scope.row.ship_date) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="TEXT.common.fields.status" width="120">
|
||||
<template #default="scope">{{ displayEnum(TEXT.enums.shipmentStatus, scope.row.status) }}</template>
|
||||
<el-table-column prop="receive_date" :label="TEXT.common.fields.receiveDate" width="140">
|
||||
<template #default="scope">{{ displayDate(scope.row.receive_date) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.common.labels.actions" width="200">
|
||||
<el-table-column prop="quantity" :label="TEXT.common.fields.quantity" width="120" align="right">
|
||||
<template #default="scope">{{ typeof scope.row.quantity === "number" ? scope.row.quantity : TEXT.common.fallback }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="batch_no" :label="TEXT.common.fields.batchNo" min-width="140">
|
||||
<template #default="scope">{{ scope.row.batch_no || TEXT.common.fallback }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" :label="TEXT.common.fields.status" width="120">
|
||||
<template #default="scope">
|
||||
<el-button link type="primary" size="small" @click="goDetail(scope.row.id)">{{ TEXT.common.actions.view }}</el-button>
|
||||
<el-button link type="primary" size="small" @click="goEdit(scope.row.id)">{{ TEXT.common.actions.edit }}</el-button>
|
||||
<el-button link type="danger" size="small" @click="remove(scope.row)">{{ TEXT.common.actions.delete }}</el-button>
|
||||
<el-tag :type="statusType(scope.row.status)" disable-transitions>
|
||||
{{ displayEnum(TEXT.enums.shipmentStatus, scope.row.status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" :label="TEXT.common.fields.remark" min-width="160" show-overflow-tooltip>
|
||||
<template #default="scope">{{ scope.row.remark || TEXT.common.fallback }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="TEXT.common.labels.actions" width="120" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button link type="danger" size="small" @click.stop="remove(scope.row)">{{ TEXT.common.actions.delete }}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -64,6 +93,7 @@ import { useRouter } from "vue-router";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useStudyStore } from "../../store/study";
|
||||
import { listDrugShipments, deleteDrugShipment } from "../../api/drugShipments";
|
||||
import { fetchSites } from "../../api/sites";
|
||||
import { displayDate, displayEnum } from "../../utils/display";
|
||||
import StateEmpty from "../../components/StateEmpty.vue";
|
||||
import { TEXT } from "../../locales";
|
||||
@@ -72,20 +102,32 @@ const router = useRouter();
|
||||
const study = useStudyStore();
|
||||
const loading = ref(false);
|
||||
const items = ref<any[]>([]);
|
||||
const sites = ref<any[]>([]);
|
||||
const filters = reactive({
|
||||
site_name: "",
|
||||
tracking_no: "",
|
||||
center_id: "",
|
||||
direction: "",
|
||||
status: "",
|
||||
});
|
||||
|
||||
const loadSites = async () => {
|
||||
const studyId = study.currentStudy?.id;
|
||||
if (!studyId) return;
|
||||
try {
|
||||
const { data } = await fetchSites(studyId, { limit: 500 });
|
||||
sites.value = Array.isArray(data) ? data : data.items || [];
|
||||
} catch {
|
||||
sites.value = [];
|
||||
}
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
const studyId = study.currentStudy?.id;
|
||||
if (!studyId) return;
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await listDrugShipments(studyId, {
|
||||
site_name: filters.site_name || undefined,
|
||||
tracking_no: filters.tracking_no || undefined,
|
||||
center_id: filters.center_id || undefined,
|
||||
direction: filters.direction || undefined,
|
||||
status: filters.status || undefined,
|
||||
});
|
||||
items.value = Array.isArray(data) ? data : data.items || [];
|
||||
@@ -97,15 +139,35 @@ const load = async () => {
|
||||
};
|
||||
|
||||
const resetFilters = () => {
|
||||
filters.site_name = "";
|
||||
filters.tracking_no = "";
|
||||
filters.center_id = "";
|
||||
filters.direction = "";
|
||||
filters.status = "";
|
||||
load();
|
||||
};
|
||||
|
||||
const goNew = () => router.push("/drug/shipments/new");
|
||||
const goDetail = (id: string) => router.push(`/drug/shipments/${id}`);
|
||||
const goEdit = (id: string) => router.push(`/drug/shipments/${id}/edit`);
|
||||
const onRowClick = (row: any) => {
|
||||
if (!row?.id) return;
|
||||
goDetail(row.id);
|
||||
};
|
||||
|
||||
const statusType = (status: string) => {
|
||||
switch (status) {
|
||||
case "PENDING":
|
||||
return "info";
|
||||
case "IN_TRANSIT":
|
||||
return "primary";
|
||||
case "SIGNED":
|
||||
return "success";
|
||||
case "RETURNED":
|
||||
return "warning";
|
||||
case "EXCEPTION":
|
||||
return "danger";
|
||||
default:
|
||||
return "info";
|
||||
}
|
||||
};
|
||||
|
||||
const remove = async (row: any) => {
|
||||
const studyId = study.currentStudy?.id;
|
||||
@@ -121,7 +183,10 @@ const remove = async (row: any) => {
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(load);
|
||||
onMounted(async () => {
|
||||
await loadSites();
|
||||
load();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user