feat: add project-scoped work assistant results

This commit is contained in:
2026-07-21 04:44:15 +08:00
parent 53b15e506c
commit 4536d607e3
75 changed files with 3281 additions and 507 deletions
@@ -6,6 +6,7 @@
<text class="memory-tag success">{{ targetLabel }}</text>
</view>
<text class="saved-copy">{{ completedCopy }}</text>
<button v-if="props.candidate.memoryType === 'CASE'" class="contribution-action" @click="goContribution">整理成成果投稿</button>
</template>
<template v-else-if="candidate.status === 'DRAFT' || candidate.status === 'NEEDS_INPUT'">
@@ -37,6 +38,10 @@
</view>
<view v-else class="memory-rows">
<view class="memory-row">
<text class="row-label">当前项目</text>
<text class="row-value">{{ projectLabel }}</text>
</view>
<view class="memory-row">
<text class="row-label">对象</text>
<text class="row-value">{{ subjectLabel || '待补充' }}</text>
@@ -54,8 +59,16 @@
<text class="row-value">{{ assistantCandidate ? '由你确认' : targetLabel }}</text>
</view>
<view class="memory-row">
<text class="row-label">记录时间</text>
<text class="row-value">{{ displayDate(draft.occurredAt) }}</text>
<text class="row-label">发生日期</text>
<text class="row-value">{{ draft.workDate || displayDate(draft.occurredAt).slice(0, 10) }}</text>
</view>
<view class="memory-row">
<text class="row-label">原始来源</text>
<text class="row-value">{{ sourceLabel }}</text>
</view>
<view class="memory-row">
<text class="row-label">处理状态</text>
<text class="row-value">{{ businessStatusLabel }}</text>
</view>
</view>
@@ -91,7 +104,7 @@
class="memory-action scope private"
:disabled="submitting"
@click="confirm('PRIVATE')"
>仅自己保存</button>
>确认记录</button>
<button
v-if="assistantCandidate"
class="memory-action scope company"
@@ -125,8 +138,10 @@ import { currentAccountKey } from '@/services/request-scope';
const props = withDefaults(defineProps<{
candidate: MemoryCandidate;
compact?: boolean;
projectName?: string;
}>(), {
compact: false
compact: false,
projectName: ''
});
const emit = defineEmits<{
@@ -147,7 +162,9 @@ const cloneDraft = (value: MemoryCandidateDraft): MemoryCandidateDraft => ({
detail: value.detail || '',
status: value.status || 'OPEN',
occurredAt: value.occurredAt || null,
followUpAt: value.followUpAt || null
followUpAt: value.followUpAt || null,
workDate: value.workDate || (value.occurredAt || '').slice(0, 10),
businessStatus: value.businessStatus || 'RECORDED'
});
const draft = ref(cloneDraft(props.candidate.draft));
@@ -168,6 +185,20 @@ const missingProjectLocationLabels = computed(() => [
{ label: '房号', value: draft.value.roomNo }
].filter((item) => !item.value?.trim()).map((item) => item.label));
const assistantCandidate = computed(() => props.candidate.targetDomain === 'ASSISTANT_CAPTURE');
const projectLabel = computed(() => props.projectName || (draft.value.projectCode ? '当前授权项目' : '个人记录'));
const sourceLabel = computed(() => ({
TEXT: '文字',
VOICE: '原始语音与转写',
IMAGE: '现场照片',
VIDEO: '现场视频'
}[props.candidate.source?.sourceType || 'TEXT'] || '文字'));
const businessStatusLabel = computed(() => ({
RECORDED: '已记录',
PENDING: '待跟进',
IN_PROGRESS: '处理中',
COMPLETED: '已完成',
VOID: '已作废'
}[draft.value.businessStatus || 'RECORDED'] || '已记录'));
const memoryTypeLabel = computed(() => ({
RESIDENT_NEED: '住户需求',
@@ -177,6 +208,9 @@ const memoryTypeLabel = computed(() => ({
PROJECT_NOTE: '工作记录',
SERVICE_FACT: '服务事实',
FOLLOW_UP: '待跟进事项',
ATTENDANCE: '到岗/晨会记录',
INSPECTION: '巡检问题',
SERVICE_RECORD: '业主服务记录',
PERSONAL_NOTE: '个人想法'
}[props.candidate.memoryType] || '工作事项'));
@@ -187,8 +221,8 @@ const targetLabel = computed(() => ({
}[confirmedTarget.value || props.candidate.targetDomain] || '我的资料与记忆'));
const completedCopy = computed(() => confirmedTarget.value === 'COMPANY'
? '已提交公司处理,可在“我的资料与记忆”中查看流转状态。'
: '这条信息已保存,后续工作助手可按权限查询。');
? '已进入公司待流转,可在“我的资料与记忆”中查看状态。'
: '这条信息已记录,并会计入对应项目的今日工作成果。');
const terminalStatusTitle = computed(() => ({
CONFIRMED: '这条事项已保存',
@@ -282,6 +316,11 @@ const dismiss = async () => {
}
};
const goContribution = () => {
uni.setStorageSync('aihr_work_report_prefill', draft.value.summary || draft.value.detail || '');
uni.navigateTo({ url: '/pages/user/report/index' });
};
watch(
() => [props.candidate.id, props.candidate.version] as const,
() => {
@@ -312,6 +351,18 @@ watch(
box-shadow: none;
}
.contribution-action {
margin-top: 12px;
padding: 9px 14px;
border: 1px solid #ef3340;
border-radius: 16px;
background: #fff;
color: #d9081c;
font-size: 14px;
}
.contribution-action::after { border: 0; }
.memory-head,
.memory-row,
.field-row,