237 lines
6.4 KiB
Vue
237 lines
6.4 KiB
Vue
<script setup>
|
||
/**
|
||
* 超时响应:响应超时小时配置。
|
||
* 海运 / 空运 / 陆运同页自上而下分块展示,只允许改超时小时;无增删、无 Tab。
|
||
* 「编码」= timeoutCode,每条唯一(如 sea_sales / land_hk_express),供智能体单码调用;只读。
|
||
* roleCode(销售/产品)仅为角色维度,可重复,不是调用主键。
|
||
* 陆运产品运输分类 5 项(与线路解耦);不展示线路类别。
|
||
* 数据来自主账 inq_response_timeout;本页不做工单超时判定。
|
||
*/
|
||
import { computed, onMounted, ref } from 'vue';
|
||
import { message } from 'ant-design-vue';
|
||
import {
|
||
getTimeoutListAll,
|
||
saveTimeoutHours,
|
||
} from '/@/views/inquiry/systemSetting/systemSetting.api';
|
||
import { usePermission } from '/@/hooks/web/usePermission';
|
||
import { InquiryBtnPerm } from '/@/views/inquiry/shared/inquiryPagePerms';
|
||
|
||
const { hasPermission } = usePermission();
|
||
const canEditTimeout = () => hasPermission(InquiryBtnPerm.timeoutEdit);
|
||
|
||
/** 分块顺序:海运 → 空运 → 陆运(产品确认同页分块) */
|
||
const MODE_BLOCKS = [
|
||
{ code: 'SEA', title: '海运' },
|
||
{ code: 'AIR', title: '空运' },
|
||
{ code: 'LAND', title: '陆运' },
|
||
];
|
||
|
||
const ROLE_LABEL = {
|
||
sales: '销售',
|
||
product: '产品',
|
||
air_route: '航线',
|
||
};
|
||
|
||
const rows = ref([]);
|
||
const loading = ref(false);
|
||
const savingId = ref('');
|
||
/** 编辑中的草稿小时数(按 id),失焦或回车时提交 */
|
||
const draftHours = ref({});
|
||
|
||
/** 按运输方式分组,块内按 sort_no 排 */
|
||
const blocks = computed(() =>
|
||
MODE_BLOCKS.map((b) => ({
|
||
...b,
|
||
rows: rows.value
|
||
.filter((r) => r.transportMode === b.code)
|
||
.sort((a, c) => (a.sortNo || 0) - (c.sortNo || 0)),
|
||
})),
|
||
);
|
||
|
||
function roleLabel(code) {
|
||
return ROLE_LABEL[code] || code;
|
||
}
|
||
|
||
function displayOrDash(v) {
|
||
const s = (v || '').trim();
|
||
return s || '—';
|
||
}
|
||
|
||
/** 唯一业务编码;缺省时提示需迁移 */
|
||
function timeoutCodeOf(row) {
|
||
const c = String(row.timeoutCode || '').trim();
|
||
return c || '—';
|
||
}
|
||
|
||
async function loadList() {
|
||
loading.value = true;
|
||
try {
|
||
const res = await getTimeoutListAll();
|
||
const list = Array.isArray(res) ? res : res?.result || res?.records || [];
|
||
rows.value = list.map((r) => ({
|
||
...r,
|
||
timeoutHours: Number(r.timeoutHours ?? 0),
|
||
}));
|
||
const next = {};
|
||
for (const r of rows.value) {
|
||
next[r.id] = r.timeoutHours;
|
||
}
|
||
draftHours.value = next;
|
||
} catch (e) {
|
||
message.error(e?.message || '加载超时配置失败');
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
}
|
||
|
||
onMounted(() => loadList());
|
||
|
||
function onHoursInput(id, raw) {
|
||
const n = Number(raw);
|
||
draftHours.value[id] = Number.isFinite(n) && n >= 0 ? n : 0;
|
||
}
|
||
|
||
async function commitHours(row) {
|
||
const hours = draftHours.value[row.id];
|
||
if (hours == null || !Number.isFinite(hours) || hours < 0) {
|
||
message.warning('超时小时须为 ≥0 的数字,支持小数');
|
||
draftHours.value[row.id] = row.timeoutHours;
|
||
return;
|
||
}
|
||
if (hours === row.timeoutHours) {
|
||
return;
|
||
}
|
||
savingId.value = row.id;
|
||
try {
|
||
await saveTimeoutHours(row.id, hours);
|
||
row.timeoutHours = hours;
|
||
message.success('已保存');
|
||
} catch (e) {
|
||
message.error(e?.message || '保存失败');
|
||
draftHours.value[row.id] = row.timeoutHours;
|
||
} finally {
|
||
savingId.value = '';
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<div class="sys-setting-page">
|
||
<div class="toolbar-bar">
|
||
<span class="hint">固定规则,仅可改超时小时(支持小数,0=不监测);编码唯一只读,供智能体调用;失焦或回车保存</span>
|
||
</div>
|
||
|
||
<div v-if="loading" style="padding: 24px; color: #94a3b8">加载中…</div>
|
||
|
||
<template v-else>
|
||
<section v-for="block in blocks" :key="block.code" class="section-card mode-block">
|
||
<div class="section-card-header">
|
||
<div class="section-card-title">{{ block.title }}</div>
|
||
</div>
|
||
<div class="table-wrap">
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th style="width: 88px">角色</th>
|
||
<th style="width: 160px" title="每条唯一;智能体用此码取超时小时">编码</th>
|
||
<th>运输分类</th>
|
||
<th style="width: 140px">超时设置(小时)</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr v-if="!block.rows.length">
|
||
<td colspan="4" style="color: #94a3b8">暂无配置</td>
|
||
</tr>
|
||
<tr v-for="row in block.rows" :key="row.id">
|
||
<td>{{ roleLabel(row.roleCode) }}</td>
|
||
<td>
|
||
<code class="role-code" :title="'智能体调用编码:' + timeoutCodeOf(row)">{{
|
||
timeoutCodeOf(row)
|
||
}}</code>
|
||
</td>
|
||
<td>{{ displayOrDash(row.transportType) }}</td>
|
||
<td>
|
||
<div class="hours-cell">
|
||
<input
|
||
class="field hours-input"
|
||
type="number"
|
||
min="0"
|
||
step="0.01"
|
||
:value="draftHours[row.id]"
|
||
:disabled="!canEditTimeout() || savingId === row.id"
|
||
@input="onHoursInput(row.id, $event.target.value)"
|
||
@blur="commitHours(row)"
|
||
@keydown.enter.prevent="commitHours(row)"
|
||
/>
|
||
<span class="hours-unit">h</span>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.sys-setting-page {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
}
|
||
|
||
.toolbar-bar {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
}
|
||
|
||
.hint {
|
||
font-size: 11px;
|
||
color: #94a3b8;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.mode-block {
|
||
margin: 0;
|
||
}
|
||
|
||
.mode-block .table-wrap {
|
||
border: none;
|
||
border-radius: 0;
|
||
box-shadow: none;
|
||
}
|
||
|
||
.hours-cell {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
}
|
||
|
||
.hours-input {
|
||
width: 88px;
|
||
min-height: 32px;
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
.hours-unit {
|
||
font-size: 11px;
|
||
color: #667085;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.role-code {
|
||
display: inline-block;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
color: #344054;
|
||
background: #f2f4f7;
|
||
border: 1px solid #e4e7ec;
|
||
}
|
||
</style>
|