完善字符串类型的体验

This commit is contained in:
BBIT-Kai
2026-04-14 17:50:27 +08:00
parent 44181bcf5a
commit 55a2490e14
8 changed files with 92 additions and 16 deletions
+2 -2
View File
@@ -7,5 +7,5 @@ ktor:
traceability: traceability:
# 访问主服务的地址 # 访问主服务的地址
core-base-url: "http://127.0.0.1:8089" # 开发 # core-base-url: "http://127.0.0.1:8089" # 开发
# core-base-url: "https://ai.ronsunny.cn:8090/api" # 生产 core-base-url: "https://ai.ronsunny.cn:8090/api" # 生产
@@ -85,6 +85,11 @@ a {
line-height: 1.75; line-height: 1.75;
} }
.timeline-item__body strong {
white-space: pre-wrap;
word-break: break-word;
}
.hero p { .hero p {
margin: 12px 0 0; margin: 12px 0 0;
} }
@@ -135,6 +140,7 @@ a {
margin-top: 8px; margin-top: 8px;
line-height: 1.6; line-height: 1.6;
word-break: break-word; word-break: break-word;
white-space: pre-wrap;
} }
.hero__aside { .hero__aside {
@@ -288,6 +288,19 @@ fun Application.Traceability(config: AppConfig) {
} }
call.respond(BaseResponse(message = "批次已发布", data = data)) call.respond(BaseResponse(message = "批次已发布", data = data))
} }
post("/{id}/scan-count/reset") {
val id = parseUuidOrNull(call.parameters["id"])
if (id == null) {
call.respond(HttpStatusCode.BadRequest, BaseResponse(status = false, message = "批次ID无效", data = null))
return@post
}
val data = TraceabilityDao.resetBatchScanCount(id)
if (data == null) {
call.respond(HttpStatusCode.NotFound, BaseResponse(status = false, message = "批次不存在", data = null))
return@post
}
call.respond(BaseResponse(message = "扫码次数已重置", data = data))
}
} }
route("/feedback") { route("/feedback") {
@@ -764,6 +764,16 @@ object TraceabilityDao {
getBatch(batchId) getBatch(batchId)
} }
fun resetBatchScanCount(batchId: UUID): TraceBatchDetailResponse? = transaction {
val now = timestampLiteral(nowInstant())
val updated = TraceabilityBatchesTable.update({ TraceabilityBatchesTable.id eq batchId }) {
it[scanCount] = 0
it[updatedAt] = now
}
if (updated == 0) return@transaction null
getBatch(batchId)
}
fun getPublicDetailByCode( fun getPublicDetailByCode(
batchCode: String, batchCode: String,
increaseScan: Boolean = false, increaseScan: Boolean = false,
@@ -341,6 +341,10 @@ export function publishTraceabilityBatch(id: string) {
return requestClient.post(`/traceability/batches/${id}/publish`); return requestClient.post(`/traceability/batches/${id}/publish`);
} }
export function resetTraceabilityBatchScanCount(id: string) {
return requestClient.post(`/traceability/batches/${id}/scan-count/reset`);
}
export function getTraceabilityPublicDetail(code: string) { export function getTraceabilityPublicDetail(code: string) {
return requestClient.get<TraceabilityApi.PublicDetail>( return requestClient.get<TraceabilityApi.PublicDetail>(
`/traceability/public/by-code/${code}`, `/traceability/public/by-code/${code}`,
@@ -275,7 +275,10 @@ onMounted(loadBatches);
>打开地图</a >打开地图</a
> >
</div> </div>
<strong v-else :style="getFieldDisplayStyle(entry.field)"> <strong
v-else
:style="getFieldDisplayStyle(entry.field)"
>
{{ formatFieldValue(entry.value) }} {{ formatFieldValue(entry.value) }}
</strong> </strong>
</div> </div>
@@ -349,10 +352,7 @@ onMounted(loadBatches);
>打开地图</a >打开地图</a
> >
</div> </div>
<strong <strong v-else :style="getFieldDisplayStyle(entry.field)">
v-else
:style="getFieldDisplayStyle(entry.field)"
>
{{ formatFieldValue(entry.value) }} {{ formatFieldValue(entry.value) }}
</strong> </strong>
</div> </div>
@@ -549,6 +549,7 @@ onMounted(loadBatches);
.access-card strong { .access-card strong {
line-height: 1.6; line-height: 1.6;
word-break: break-word; word-break: break-word;
white-space: pre-wrap;
} }
.section-stack { .section-stack {
@@ -578,6 +579,11 @@ onMounted(loadBatches);
color: #6b7280; color: #6b7280;
} }
.timeline-card strong {
white-space: pre-wrap;
word-break: break-word;
}
.kv-grid { .kv-grid {
display: grid; display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -27,6 +27,7 @@ import {
getTraceabilityBatches, getTraceabilityBatches,
getTraceabilityTemplates, getTraceabilityTemplates,
publishTraceabilityBatch, publishTraceabilityBatch,
resetTraceabilityBatchScanCount,
updateTraceabilityBatchStep, updateTraceabilityBatchStep,
uploadTraceabilityImage, uploadTraceabilityImage,
} from '#/api'; } from '#/api';
@@ -435,6 +436,29 @@ async function saveStep() {
} }
} }
function resetBatchScanCount() {
if (!batchDetail.value?.id) return;
Modal.confirm({
title: '重置扫码次数',
content: `确认将批次“${batchDetail.value.batchName || batchDetail.value.batchCode}”的扫码次数重置为 0 吗?`,
okText: '重置',
cancelText: '取消',
async onOk() {
saving.value = true;
try {
const detail = await resetTraceabilityBatchScanCount(batchDetail.value!.id);
if (detail) {
applyBatch(detail);
message.success('扫码次数已重置');
await loadLists();
}
} finally {
saving.value = false;
}
},
});
}
onMounted(async () => { onMounted(async () => {
await loadLists(); await loadLists();
}); });
@@ -509,14 +533,23 @@ onMounted(async () => {
title="发布信息" title="发布信息"
> >
<template #extra> <template #extra>
<Button <Space>
v-if="batchDetail.publicUrl" <Button
size="small" v-if="batchDetail.publicUrl"
type="primary" size="small"
@click="downloadBatchQrCode" type="primary"
> @click="downloadBatchQrCode"
保存二维码 >
</Button> 保存二维码
</Button>
<Button
v-if="batchDetail.publicUrl"
size="small"
@click="resetBatchScanCount"
>
重置扫码次数
</Button>
</Space>
</template> </template>
<div class="publish-panel"> <div class="publish-panel">
<div> <div>
@@ -755,11 +788,12 @@ onMounted(async () => {
(value) => updateFieldValue(field, value) (value) => updateFieldValue(field, value)
" "
/> />
<Input <Input.TextArea
v-else-if="field.type === 'string'" v-else-if="field.type === 'string'"
:disabled="isFieldValueLocked(field)" :disabled="isFieldValueLocked(field)"
:placeholder="field.placeholder || '请输入内容'" :placeholder="field.placeholder || '请输入内容'"
:value="currentStep.values[field.key]" :value="currentStep.values[field.key]"
:auto-size="{ minRows: 2, maxRows: 6 }"
@update:value=" @update:value="
(value) => updateFieldValue(field, value) (value) => updateFieldValue(field, value)
" "
@@ -1261,6 +1295,8 @@ onMounted(async () => {
font-size: 12px; font-size: 12px;
border-top: 1px dashed #e4e9f2; border-top: 1px dashed #e4e9f2;
padding-top: 10px; padding-top: 10px;
white-space: pre-wrap;
word-break: break-word;
} }
.field-fixed-tip { .field-fixed-tip {
@@ -638,6 +638,7 @@ onMounted(loadPreviews);
<Select v-else-if="currentField.type === 'select'" :options="(currentField.options ?? []).map((item) => ({ label: item, value: item }))" :value="getPreviewFieldValue(currentNode, currentField)" style="width: 100%" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" /> <Select v-else-if="currentField.type === 'select'" :options="(currentField.options ?? []).map((item) => ({ label: item, value: item }))" :value="getPreviewFieldValue(currentNode, currentField)" style="width: 100%" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" />
<Select v-else-if="currentField.type === 'multi_select'" mode="multiple" :options="(currentField.options ?? []).map((item) => ({ label: item, value: item }))" :value="getPreviewFieldValue(currentNode, currentField) ?? []" style="width: 100%" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" /> <Select v-else-if="currentField.type === 'multi_select'" mode="multiple" :options="(currentField.options ?? []).map((item) => ({ label: item, value: item }))" :value="getPreviewFieldValue(currentNode, currentField) ?? []" style="width: 100%" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" />
<Input.TextArea v-else-if="currentField.type === 'json'" :value="typeof getPreviewFieldValue(currentNode, currentField) === 'string' ? getPreviewFieldValue(currentNode, currentField) : JSON.stringify(getPreviewFieldValue(currentNode, currentField) ?? {}, null, 2)" :auto-size="{ minRows: 3, maxRows: 6 }" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" /> <Input.TextArea v-else-if="currentField.type === 'json'" :value="typeof getPreviewFieldValue(currentNode, currentField) === 'string' ? getPreviewFieldValue(currentNode, currentField) : JSON.stringify(getPreviewFieldValue(currentNode, currentField) ?? {}, null, 2)" :auto-size="{ minRows: 3, maxRows: 6 }" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" />
<Input.TextArea v-else-if="currentField.type === 'string'" :value="getPreviewFieldValue(currentNode, currentField)" :auto-size="{ minRows: 2, maxRows: 6 }" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" />
<Input v-else :value="getPreviewFieldValue(currentNode, currentField)" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" /> <Input v-else :value="getPreviewFieldValue(currentNode, currentField)" @update:value="(value) => updateFieldValue(currentNode!, currentField!, value)" />
</Col> </Col>
</Row> </Row>