refactor: update orchestrator service and travel form view
- services/orchestrator.py: update orchestrator service - views/TravelReimbursementCreateView.vue: update travel form view - views/scripts/TravelReimbursementCreateView.js: update travel form script
This commit is contained in:
@@ -1257,8 +1257,9 @@ class OrchestratorService:
|
|||||||
|
|
||||||
if employee is not None:
|
if employee is not None:
|
||||||
add_condition("employee_id", employee.id)
|
add_condition("employee_id", employee.id)
|
||||||
add_condition("employee_name", employee.name)
|
|
||||||
add_condition("employee_name", employee.email)
|
add_condition("employee_name", employee.email)
|
||||||
|
if self._employee_name_is_unique(employee):
|
||||||
|
add_condition("employee_name", employee.name)
|
||||||
else:
|
else:
|
||||||
add_condition("employee_id", normalized_user_id)
|
add_condition("employee_id", normalized_user_id)
|
||||||
add_condition("employee_name", normalized_user_id)
|
add_condition("employee_name", normalized_user_id)
|
||||||
@@ -1268,6 +1269,19 @@ class OrchestratorService:
|
|||||||
return conditions, "你的报销单"
|
return conditions, "你的报销单"
|
||||||
return conditions, "当前用户的报销单"
|
return conditions, "当前用户的报销单"
|
||||||
|
|
||||||
|
def _employee_name_is_unique(self, employee: Employee) -> bool:
|
||||||
|
normalized_name = str(employee.name or "").strip()
|
||||||
|
if not normalized_name:
|
||||||
|
return False
|
||||||
|
|
||||||
|
same_name_count = int(
|
||||||
|
self.db.scalar(
|
||||||
|
select(func.count()).select_from(Employee).where(Employee.name == normalized_name)
|
||||||
|
)
|
||||||
|
or 0
|
||||||
|
)
|
||||||
|
return same_name_count == 1
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _has_privileged_expense_query_access(context_json: dict[str, Any]) -> bool:
|
def _has_privileged_expense_query_access(context_json: dict[str, Any]) -> bool:
|
||||||
role_codes = {
|
role_codes = {
|
||||||
|
|||||||
@@ -120,6 +120,100 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="message.role === 'assistant' && !message.reviewPayload && message.queryPayload?.resultType === 'expense_claim_list'"
|
||||||
|
class="message-detail-block expense-query-block"
|
||||||
|
>
|
||||||
|
<strong>{{ message.queryPayload.recentWindowApplied ? '近 10 日单据' : '单据明细' }}</strong>
|
||||||
|
|
||||||
|
<p v-if="buildExpenseQueryWindowLabel(message.queryPayload)" class="expense-query-window-label">
|
||||||
|
{{ buildExpenseQueryWindowLabel(message.queryPayload) }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div v-if="message.queryPayload.statusGroups?.length" class="expense-query-summary-row">
|
||||||
|
<span
|
||||||
|
v-for="item in message.queryPayload.statusGroups"
|
||||||
|
:key="`${message.id}-${item.key}`"
|
||||||
|
class="expense-query-summary-chip"
|
||||||
|
:class="item.key"
|
||||||
|
>
|
||||||
|
{{ item.label }} {{ item.count }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="message.queryPayload.records?.length" class="expense-query-record-list compact">
|
||||||
|
<button
|
||||||
|
v-for="record in getExpenseQueryVisibleRecords(message.queryPayload)"
|
||||||
|
:key="`${message.id}-${record.claimId}`"
|
||||||
|
type="button"
|
||||||
|
class="expense-query-record-card"
|
||||||
|
@click="openExpenseQueryRecord(record)"
|
||||||
|
>
|
||||||
|
<div class="expense-query-record-main">
|
||||||
|
<div class="expense-query-record-top">
|
||||||
|
<strong>{{ record.claimNo }}</strong>
|
||||||
|
<span class="expense-query-record-status" :class="record.statusGroup || 'other'">
|
||||||
|
{{ record.statusLabel }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<p>{{ record.summary }}</p>
|
||||||
|
<div class="expense-query-record-meta">
|
||||||
|
<span>{{ record.expenseTypeLabel }}</span>
|
||||||
|
<span>{{ record.dateDisplay }}</span>
|
||||||
|
<span>{{ record.amountDisplay }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<i class="mdi mdi-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="getExpenseQueryTotalPages(message.queryPayload) > 1"
|
||||||
|
class="expense-query-pager"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="expense-query-pager-btn"
|
||||||
|
:disabled="getExpenseQueryActivePage(message.queryPayload) === 1"
|
||||||
|
aria-label="上一页"
|
||||||
|
@click="shiftExpenseQueryPage(message, -1)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-chevron-left"></i>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="expense-query-pager-dots" aria-label="单据分页">
|
||||||
|
<button
|
||||||
|
v-for="page in getExpenseQueryTotalPages(message.queryPayload)"
|
||||||
|
:key="`${message.id}-query-page-${page}`"
|
||||||
|
type="button"
|
||||||
|
class="expense-query-pager-dot"
|
||||||
|
:class="{ active: getExpenseQueryActivePage(message.queryPayload) === page }"
|
||||||
|
:aria-label="`第 ${page} 页`"
|
||||||
|
@click="setExpenseQueryPage(message, page)"
|
||||||
|
></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="expense-query-pager-btn"
|
||||||
|
:disabled="getExpenseQueryActivePage(message.queryPayload) === getExpenseQueryTotalPages(message.queryPayload)"
|
||||||
|
aria-label="下一页"
|
||||||
|
@click="shiftExpenseQueryPage(message, 1)"
|
||||||
|
>
|
||||||
|
<i class="mdi mdi-chevron-right"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="expense-query-empty">
|
||||||
|
<i class="mdi mdi-file-search-outline"></i>
|
||||||
|
<span>当前没有可直接展开的近期待办单据。</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p v-if="buildExpenseQueryHint(message.queryPayload)" class="expense-query-hint">
|
||||||
|
{{ buildExpenseQueryHint(message.queryPayload) }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="message.role === 'assistant' && message.reviewPayload" class="message-detail-block review-message-block">
|
<div v-if="message.role === 'assistant' && message.reviewPayload" class="message-detail-block review-message-block">
|
||||||
<div class="review-card-shell">
|
<div class="review-card-shell">
|
||||||
<div class="review-card-head">
|
<div class="review-card-head">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
|
import ConfirmDialog from '../../components/shared/ConfirmDialog.vue'
|
||||||
import { useSystemState } from '../../composables/useSystemState.js'
|
import { useSystemState } from '../../composables/useSystemState.js'
|
||||||
@@ -151,6 +152,7 @@ const MAX_ATTACHMENTS = 10
|
|||||||
const MAX_OCR_DOCUMENTS = 10
|
const MAX_OCR_DOCUMENTS = 10
|
||||||
const VISIBLE_ATTACHMENT_CHIPS = 2
|
const VISIBLE_ATTACHMENT_CHIPS = 2
|
||||||
const COMPOSER_MAX_ROWS = 5
|
const COMPOSER_MAX_ROWS = 5
|
||||||
|
const EXPENSE_QUERY_PAGE_SIZE = 5
|
||||||
const SESSION_TYPE_EXPENSE = 'expense'
|
const SESSION_TYPE_EXPENSE = 'expense'
|
||||||
const SESSION_TYPE_KNOWLEDGE = 'knowledge'
|
const SESSION_TYPE_KNOWLEDGE = 'knowledge'
|
||||||
const HOT_KNOWLEDGE_QUESTIONS = [
|
const HOT_KNOWLEDGE_QUESTIONS = [
|
||||||
@@ -199,6 +201,7 @@ function createMessage(role, text, attachments = [], extras = {}) {
|
|||||||
meta: [],
|
meta: [],
|
||||||
citations: [],
|
citations: [],
|
||||||
suggestedActions: [],
|
suggestedActions: [],
|
||||||
|
queryPayload: null,
|
||||||
draftPayload: null,
|
draftPayload: null,
|
||||||
reviewPayload: null,
|
reviewPayload: null,
|
||||||
riskFlags: [],
|
riskFlags: [],
|
||||||
@@ -625,8 +628,50 @@ function resolveKnowledgeRankTone(index) {
|
|||||||
return 'default'
|
return 'default'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseConversationMessageSequence(message) {
|
||||||
|
const messageJson = message?.message_json || message?.messageJson || {}
|
||||||
|
const sequence = Number.parseInt(messageJson?.sequence, 10)
|
||||||
|
return Number.isFinite(sequence) && sequence > 0 ? sequence : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseConversationMessageTime(message) {
|
||||||
|
const rawValue = message?.created_at || message?.createdAt || ''
|
||||||
|
const timestamp = new Date(rawValue).getTime()
|
||||||
|
return Number.isFinite(timestamp) ? timestamp : Number.MAX_SAFE_INTEGER
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveConversationMessageRolePriority(message) {
|
||||||
|
return String(message?.role || '').trim() === 'user' ? 0 : 1
|
||||||
|
}
|
||||||
|
|
||||||
|
function sortConversationMessages(messages) {
|
||||||
|
return [...(Array.isArray(messages) ? messages : [])].sort((left, right) => {
|
||||||
|
const leftSequence = parseConversationMessageSequence(left)
|
||||||
|
const rightSequence = parseConversationMessageSequence(right)
|
||||||
|
if (leftSequence !== null && rightSequence !== null && leftSequence !== rightSequence) {
|
||||||
|
return leftSequence - rightSequence
|
||||||
|
}
|
||||||
|
|
||||||
|
const timeDiff = parseConversationMessageTime(left) - parseConversationMessageTime(right)
|
||||||
|
if (timeDiff !== 0) {
|
||||||
|
return timeDiff
|
||||||
|
}
|
||||||
|
|
||||||
|
const leftRunId = String(left?.run_id || left?.runId || '').trim()
|
||||||
|
const rightRunId = String(right?.run_id || right?.runId || '').trim()
|
||||||
|
if (leftRunId && rightRunId && leftRunId === rightRunId) {
|
||||||
|
const roleDiff = resolveConversationMessageRolePriority(left) - resolveConversationMessageRolePriority(right)
|
||||||
|
if (roleDiff !== 0) {
|
||||||
|
return roleDiff
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return String(left?.id || '').localeCompare(String(right?.id || ''))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeInitialConversationMessages(conversation) {
|
function normalizeInitialConversationMessages(conversation) {
|
||||||
const rawMessages = Array.isArray(conversation?.messages) ? conversation.messages : []
|
const rawMessages = sortConversationMessages(conversation?.messages)
|
||||||
|
|
||||||
return rawMessages.map((item) => {
|
return rawMessages.map((item) => {
|
||||||
const messageJson = item?.message_json || item?.messageJson || {}
|
const messageJson = item?.message_json || item?.messageJson || {}
|
||||||
@@ -645,6 +690,7 @@ function normalizeInitialConversationMessages(conversation) {
|
|||||||
item.role === 'assistant' && Array.isArray(result?.suggested_actions)
|
item.role === 'assistant' && Array.isArray(result?.suggested_actions)
|
||||||
? result.suggested_actions
|
? result.suggested_actions
|
||||||
: [],
|
: [],
|
||||||
|
queryPayload: item.role === 'assistant' ? normalizeExpenseQueryPayload(result?.query_payload) : null,
|
||||||
draftPayload: item.role === 'assistant' ? result?.draft_payload || messageJson?.draft_payload || null : null,
|
draftPayload: item.role === 'assistant' ? result?.draft_payload || messageJson?.draft_payload || null : null,
|
||||||
reviewPayload: item.role === 'assistant' ? result?.review_payload || null : null,
|
reviewPayload: item.role === 'assistant' ? result?.review_payload || null : null,
|
||||||
riskFlags: item.role === 'assistant' && Array.isArray(result?.risk_flags) ? result.risk_flags : []
|
riskFlags: item.role === 'assistant' && Array.isArray(result?.risk_flags) ? result.risk_flags : []
|
||||||
@@ -909,6 +955,159 @@ function formatAmountDisplay(value) {
|
|||||||
}).format(amount)
|
}).format(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeExpenseQueryStatusGroup(item) {
|
||||||
|
if (!item || typeof item !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawCount = Number(item.count || 0)
|
||||||
|
return {
|
||||||
|
key: String(item.key || 'other').trim() || 'other',
|
||||||
|
label: String(item.label || '其他状态').trim() || '其他状态',
|
||||||
|
count: Number.isFinite(rawCount) ? Math.max(0, rawCount) : 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeExpenseQueryRecord(item) {
|
||||||
|
if (!item || typeof item !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const amount = Number(item.amount || 0)
|
||||||
|
const amountValue = Number.isFinite(amount) ? amount : 0
|
||||||
|
const expenseTypeLabel = String(item.expense_type_label || item.expense_type || '报销').trim() || '报销'
|
||||||
|
const reason = String(item.reason || '').trim()
|
||||||
|
const documentDate = String(item.document_date || '').trim()
|
||||||
|
const occurredAt = String(item.occurred_at || '').trim()
|
||||||
|
|
||||||
|
return {
|
||||||
|
claimId: String(item.claim_id || '').trim(),
|
||||||
|
claimNo: String(item.claim_no || '').trim() || '未编号',
|
||||||
|
employeeName: String(item.employee_name || '').trim(),
|
||||||
|
expenseType: String(item.expense_type || '').trim(),
|
||||||
|
expenseTypeLabel,
|
||||||
|
amount: amountValue,
|
||||||
|
amountDisplay: formatAmountDisplay(amountValue),
|
||||||
|
status: String(item.status || '').trim(),
|
||||||
|
statusLabel: String(item.status_label || '处理中').trim() || '处理中',
|
||||||
|
statusGroup: String(item.status_group || 'other').trim() || 'other',
|
||||||
|
statusGroupLabel: String(item.status_group_label || '其他状态').trim() || '其他状态',
|
||||||
|
approvalStage: String(item.approval_stage || '').trim(),
|
||||||
|
documentDate,
|
||||||
|
occurredAt,
|
||||||
|
reason,
|
||||||
|
location: String(item.location || '').trim(),
|
||||||
|
summary: reason || `${expenseTypeLabel}报销`,
|
||||||
|
dateDisplay: documentDate || occurredAt || '待补充日期'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeExpenseQueryPayload(payload) {
|
||||||
|
if (!payload || typeof payload !== 'object') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const resultType = String(payload.result_type || '').trim()
|
||||||
|
if (resultType && resultType !== 'expense_claim_list') {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = (Array.isArray(payload.records) ? payload.records : [])
|
||||||
|
.map(normalizeExpenseQueryRecord)
|
||||||
|
.filter(Boolean)
|
||||||
|
const statusGroups = (Array.isArray(payload.status_groups) ? payload.status_groups : [])
|
||||||
|
.map(normalizeExpenseQueryStatusGroup)
|
||||||
|
.filter(Boolean)
|
||||||
|
|
||||||
|
const rawRecordCount = Number(payload.record_count || 0)
|
||||||
|
const rawPreviewCount = Number(payload.preview_count || records.length)
|
||||||
|
const rawOlderRecordCount = Number(payload.older_record_count || 0)
|
||||||
|
const totalAmount = Number(payload.total_amount || 0)
|
||||||
|
const rawWindowDays = Number(payload.window_days || 0)
|
||||||
|
const windowStartDate = String(payload.window_start_date || '').trim()
|
||||||
|
const windowEndDate = String(payload.window_end_date || '').trim()
|
||||||
|
|
||||||
|
return {
|
||||||
|
resultType: 'expense_claim_list',
|
||||||
|
scopeLabel: String(payload.scope_label || '报销单').trim() || '报销单',
|
||||||
|
recentWindowApplied: Boolean(payload.recent_window_applied),
|
||||||
|
windowDays:
|
||||||
|
payload.window_days === null || payload.window_days === undefined || payload.window_days === ''
|
||||||
|
? null
|
||||||
|
: (Number.isFinite(rawWindowDays) ? Math.max(1, rawWindowDays) : null),
|
||||||
|
windowStartDate: windowStartDate || '',
|
||||||
|
windowEndDate: windowEndDate || '',
|
||||||
|
recordCount: Number.isFinite(rawRecordCount) ? Math.max(0, rawRecordCount) : 0,
|
||||||
|
previewCount: Number.isFinite(rawPreviewCount) ? Math.max(0, rawPreviewCount) : records.length,
|
||||||
|
olderRecordCount: Number.isFinite(rawOlderRecordCount) ? Math.max(0, rawOlderRecordCount) : 0,
|
||||||
|
hasMoreInWindow: Boolean(payload.has_more_in_window || payload.has_more),
|
||||||
|
totalAmount: Number.isFinite(totalAmount) ? totalAmount : 0,
|
||||||
|
statusGroups,
|
||||||
|
records,
|
||||||
|
currentPage: 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildExpenseQueryWindowLabel(queryPayload) {
|
||||||
|
if (!queryPayload) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryPayload.windowStartDate && queryPayload.windowEndDate) {
|
||||||
|
return `${queryPayload.windowStartDate} 至 ${queryPayload.windowEndDate}`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryPayload.recentWindowApplied && queryPayload.windowDays) {
|
||||||
|
return `近 ${queryPayload.windowDays} 日内`
|
||||||
|
}
|
||||||
|
|
||||||
|
return '当前条件下'
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpenseQueryTotalPages(queryPayload) {
|
||||||
|
const recordCount = Array.isArray(queryPayload?.records) ? queryPayload.records.length : 0
|
||||||
|
return Math.max(1, Math.ceil(recordCount / EXPENSE_QUERY_PAGE_SIZE))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpenseQueryActivePage(queryPayload) {
|
||||||
|
const totalPages = getExpenseQueryTotalPages(queryPayload)
|
||||||
|
const rawPage = Number(queryPayload?.currentPage || 1)
|
||||||
|
if (!Number.isFinite(rawPage)) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return Math.min(Math.max(1, Math.round(rawPage)), totalPages)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExpenseQueryVisibleRecords(queryPayload) {
|
||||||
|
const records = Array.isArray(queryPayload?.records) ? queryPayload.records : []
|
||||||
|
const activePage = getExpenseQueryActivePage(queryPayload)
|
||||||
|
const start = (activePage - 1) * EXPENSE_QUERY_PAGE_SIZE
|
||||||
|
return records.slice(start, start + EXPENSE_QUERY_PAGE_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildExpenseQueryHint(queryPayload) {
|
||||||
|
if (!queryPayload) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const parts = []
|
||||||
|
const windowText = buildExpenseQueryWindowLabel(queryPayload)
|
||||||
|
|
||||||
|
if (Array.isArray(queryPayload.records) && queryPayload.records.length > EXPENSE_QUERY_PAGE_SIZE) {
|
||||||
|
parts.push(`当前共整理 ${queryPayload.records.length} 笔单据,可左右切换查看`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryPayload.hasMoreInWindow && queryPayload.previewCount < queryPayload.recordCount) {
|
||||||
|
parts.push(`${windowText}共 ${queryPayload.recordCount} 笔,当前先整理最近 ${queryPayload.previewCount} 笔`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (queryPayload.olderRecordCount > 0 && queryPayload.windowDays) {
|
||||||
|
parts.push(`另有 ${queryPayload.olderRecordCount} 笔超过 ${queryPayload.windowDays} 日的单据,请前往个人报销中心查看`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.join('。')
|
||||||
|
}
|
||||||
|
|
||||||
function countReviewPendingItems(reviewPayload) {
|
function countReviewPendingItems(reviewPayload) {
|
||||||
return resolveReviewMissingSlotCards(reviewPayload).length
|
return resolveReviewMissingSlotCards(reviewPayload).length
|
||||||
}
|
}
|
||||||
@@ -1661,6 +1860,7 @@ function buildErrorInsight(error, fileNames = []) {
|
|||||||
fileNames,
|
fileNames,
|
||||||
citations: [],
|
citations: [],
|
||||||
suggestedActions: [],
|
suggestedActions: [],
|
||||||
|
queryPayload: null,
|
||||||
draftPayload: null,
|
draftPayload: null,
|
||||||
reviewPayload: null,
|
reviewPayload: null,
|
||||||
riskFlags: [],
|
riskFlags: [],
|
||||||
@@ -1699,6 +1899,7 @@ function buildAgentInsight(payload, fileNames = [], filePreviews = []) {
|
|||||||
fileNames,
|
fileNames,
|
||||||
citations: Array.isArray(result?.citations) ? result.citations : [],
|
citations: Array.isArray(result?.citations) ? result.citations : [],
|
||||||
suggestedActions: Array.isArray(result?.suggested_actions) ? result.suggested_actions : [],
|
suggestedActions: Array.isArray(result?.suggested_actions) ? result.suggested_actions : [],
|
||||||
|
queryPayload: normalizeExpenseQueryPayload(result?.query_payload),
|
||||||
draftPayload: result?.draft_payload || null,
|
draftPayload: result?.draft_payload || null,
|
||||||
reviewPayload: result?.review_payload || null,
|
reviewPayload: result?.review_payload || null,
|
||||||
riskFlags: Array.isArray(result?.risk_flags) ? result.risk_flags : [],
|
riskFlags: Array.isArray(result?.risk_flags) ? result.risk_flags : [],
|
||||||
@@ -1743,6 +1944,7 @@ export default {
|
|||||||
},
|
},
|
||||||
emits: ['close', 'draft-saved'],
|
emits: ['close', 'draft-saved'],
|
||||||
setup(props, { emit }) {
|
setup(props, { emit }) {
|
||||||
|
const router = useRouter()
|
||||||
const { currentUser } = useSystemState()
|
const { currentUser } = useSystemState()
|
||||||
const { toast } = useToast()
|
const { toast } = useToast()
|
||||||
|
|
||||||
@@ -1816,7 +2018,7 @@ export default {
|
|||||||
if (props.entrySource === 'detail' && linkedRequest.value?.id) {
|
if (props.entrySource === 'detail' && linkedRequest.value?.id) {
|
||||||
return `例如:解释一下 ${linkedRequest.value.id} 的报销风险,或帮我生成处理意见草稿。`
|
return `例如:解释一下 ${linkedRequest.value.id} 的报销风险,或帮我生成处理意见草稿。`
|
||||||
}
|
}
|
||||||
return '例如:查一下本周报销金额、解释酒店超标风险,或根据附件生成报销草稿。'
|
return '例如:查一下近10日报销金额、解释酒店超标风险,或根据附件生成报销草稿。'
|
||||||
})
|
})
|
||||||
const currentIntentLabel = computed(() => {
|
const currentIntentLabel = computed(() => {
|
||||||
if (isKnowledgeSession.value && currentInsight.value.intent === 'welcome') {
|
if (isKnowledgeSession.value && currentInsight.value.intent === 'welcome') {
|
||||||
@@ -2395,6 +2597,37 @@ export default {
|
|||||||
emit('close')
|
emit('close')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openExpenseQueryRecord(record) {
|
||||||
|
const claimId = String(record?.claimId || '').trim()
|
||||||
|
if (!claimId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
name: 'app-request-detail',
|
||||||
|
params: { requestId: claimId }
|
||||||
|
})
|
||||||
|
emit('close')
|
||||||
|
}
|
||||||
|
|
||||||
|
function setExpenseQueryPage(message, page) {
|
||||||
|
if (!message?.queryPayload) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalPages = getExpenseQueryTotalPages(message.queryPayload)
|
||||||
|
const nextPage = Math.min(Math.max(1, Number(page || 1)), totalPages)
|
||||||
|
message.queryPayload.currentPage = nextPage
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftExpenseQueryPage(message, delta) {
|
||||||
|
if (!message?.queryPayload) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setExpenseQueryPage(message, getExpenseQueryActivePage(message.queryPayload) + Number(delta || 0))
|
||||||
|
}
|
||||||
|
|
||||||
function openDeleteSessionDialog() {
|
function openDeleteSessionDialog() {
|
||||||
if (submitting.value || reviewActionBusy.value || deleteSessionBusy.value || sessionSwitchBusy.value) {
|
if (submitting.value || reviewActionBusy.value || deleteSessionBusy.value || sessionSwitchBusy.value) {
|
||||||
return
|
return
|
||||||
@@ -2637,6 +2870,7 @@ export default {
|
|||||||
suggestedActions: Array.isArray(payload?.result?.suggested_actions)
|
suggestedActions: Array.isArray(payload?.result?.suggested_actions)
|
||||||
? payload.result.suggested_actions
|
? payload.result.suggested_actions
|
||||||
: [],
|
: [],
|
||||||
|
queryPayload: normalizeExpenseQueryPayload(payload?.result?.query_payload),
|
||||||
draftPayload: payload?.result?.draft_payload || null,
|
draftPayload: payload?.result?.draft_payload || null,
|
||||||
reviewPayload: payload?.result?.review_payload || null,
|
reviewPayload: payload?.result?.review_payload || null,
|
||||||
riskFlags: Array.isArray(payload?.result?.risk_flags) ? payload.result.risk_flags : []
|
riskFlags: Array.isArray(payload?.result?.risk_flags) ? payload.result.risk_flags : []
|
||||||
@@ -2886,6 +3120,11 @@ export default {
|
|||||||
buildReviewRiskHint,
|
buildReviewRiskHint,
|
||||||
buildReviewActionHint,
|
buildReviewActionHint,
|
||||||
buildReviewStatusTag,
|
buildReviewStatusTag,
|
||||||
|
buildExpenseQueryWindowLabel,
|
||||||
|
buildExpenseQueryHint,
|
||||||
|
getExpenseQueryActivePage,
|
||||||
|
getExpenseQueryTotalPages,
|
||||||
|
getExpenseQueryVisibleRecords,
|
||||||
resolveDocumentPreview,
|
resolveDocumentPreview,
|
||||||
triggerFileUpload,
|
triggerFileUpload,
|
||||||
handleFilesChange,
|
handleFilesChange,
|
||||||
@@ -2899,6 +3138,9 @@ export default {
|
|||||||
removeAttachedFile,
|
removeAttachedFile,
|
||||||
clearAttachedFiles,
|
clearAttachedFiles,
|
||||||
requestCloseWorkbench,
|
requestCloseWorkbench,
|
||||||
|
openExpenseQueryRecord,
|
||||||
|
setExpenseQueryPage,
|
||||||
|
shiftExpenseQueryPage,
|
||||||
openDeleteSessionDialog,
|
openDeleteSessionDialog,
|
||||||
closeDeleteSessionDialog,
|
closeDeleteSessionDialog,
|
||||||
confirmDeleteCurrentSession,
|
confirmDeleteCurrentSession,
|
||||||
|
|||||||
Reference in New Issue
Block a user