Compare commits

...
Author SHA1 Message Date
zlwuandCursor d6dc768efb fix(web-admin): 加宽归属审计记录弹窗 (#52)
历史归属维护页审计表格列较多,800px 偏挤,调整为 1100px。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-05 16:54:22 +08:00
zlwuandCursor 96518c08fb fix(web-admin): 简化项目归属维护调整弹窗 (#52)
展示原项目组/项目,录入新项目组/项目与原因;确认时静默预览并仍级联关联单据。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-05 16:54:22 +08:00
cykj 54a34b24ba fix(web-admin): 协议单扁平行列表展示所属项目与项目组 (#55)
fix(web-admin): 协议单扁平行列表展示所属项目与项目组 (#55)

Refs: #54
2026-11-05 08:47:38 +00:00
2 changed files with 83 additions and 64 deletions

No files matched your search

@@ -20,7 +20,13 @@
v-for="option in displayOptions" v-for="option in displayOptions"
:key="option.id" :key="option.id"
:value="option.id" :value="option.id"
:label="team ? option.teamName : `${option.projectName} · ${option.teamName}`" :label="
team
? option.teamName
: teamId
? option.projectName
: `${option.projectName} · ${option.teamName}`
"
:disabled="!filter && option.disabled" :disabled="!filter && option.disabled"
/> />
</el-select> </el-select>
@@ -43,6 +49,8 @@
const props = defineProps<{ const props = defineProps<{
filter?: boolean filter?: boolean
team?: boolean team?: boolean
/** When selecting projects, keep options under this team. */
teamId?: string
modelValue?: string modelValue?: string
bizType?: BizType bizType?: BizType
disabled?: boolean disabled?: boolean
@@ -54,15 +62,18 @@
ready: [value: boolean] ready: [value: boolean]
}>() }>()
const options = ref<ProjectOption[]>([]) const options = ref<ProjectOption[]>([])
const displayOptions = computed(() => const displayOptions = computed(() => {
props.team if (props.team) {
? [ return [
...new Map( ...new Map(
options.value.map((option) => [option.teamId, { ...option, id: option.teamId }]) options.value.map((option) => [option.teamId, { ...option, id: option.teamId }])
).values() ).values()
] ]
}
return props.teamId
? options.value.filter((option) => option.teamId === props.teamId)
: options.value : options.value
) })
const loading = ref(false) const loading = ref(false)
const error = ref(false), const error = ref(false),
retry = ref(0) retry = ref(0)
@@ -143,57 +143,58 @@
<el-dialog <el-dialog
v-model="visible" v-model="visible"
title="项目归属维护" title="项目归属维护"
width="720px" width="560px"
class="dialog-form-layout" class="dialog-form-layout"
append-to-body append-to-body
modal-class="protocol-dialog-mask" modal-class="protocol-dialog-mask"
:close-on-click-modal="false" :close-on-click-modal="false"
> >
<el-form label-position="top" <el-form class="form-grid-2" label-position="top">
><el-form-item label="目标项目" required <el-form-item label="原所属项目组">
><ProjectSelect <el-input :model-value="selected.teamName || '未归属'" disabled />
v-model="target" </el-form-item>
:key="selected.id" <el-form-item label="原所属项目">
<el-input :model-value="selected.projectName || '未归属'" disabled />
</el-form-item>
<el-form-item label="新项目组" required>
<ProjectSelect
:key="`team-${selected.id}`"
team
v-model="targetTeamId"
:biz-type="selected.bizType" :biz-type="selected.bizType"
@update:model-value="previewRows = []" /></el-form-item @update:model-value="onTargetTeamChange"
><el-form-item label="调整原因" required />
><el-input v-model="reason" type="textarea" :maxlength="500" /></el-form-item </el-form-item>
></el-form> <el-form-item label="新项目" required>
<el-alert <ProjectSelect
title="请先预览全部关联单据,再确认调整。关联集合或归属发生变化时需要重新预览。" :key="`project-${selected.id}-${targetTeamId}`"
type="info" v-model="target"
:closable="false" :team-id="targetTeamId"
/> :biz-type="selected.bizType"
<el-table :data="previewRows" class="list-table" stripe :disabled="!targetTeamId"
><el-table-column />
type="index" </el-form-item>
label="序号" <el-form-item label="调整原因" required class="form-col-span-2">
min-width="68" <el-input v-model="reason" type="textarea" :maxlength="500" show-word-limit />
align="center" </el-form-item>
header-align="center" /> </el-form>
<el-table-column prop="bizNo" label="关联单据" min-width="200" /><el-table-column <template #footer>
prop="projectName" <el-button @click="visible = false"
label="原项目"
min-width="160"
/></el-table>
<template #footer
><el-button @click="visible = false"
><el-icon><Close /></el-icon>取消</el-button ><el-icon><Close /></el-icon>取消</el-button
><el-button :loading="busy" @click="preview" >
><el-icon><View /></el-icon>预览关联单据</el-button <el-button
><el-button
type="primary" type="primary"
:disabled="!previewRows.length || !reason.trim()" :disabled="!target || !reason.trim()"
:loading="busy" :loading="busy"
@click="commit" @click="commit"
><el-icon><Check /></el-icon>确认调整</el-button ><el-icon><Check /></el-icon>确认调整</el-button
></template >
> </template>
</el-dialog> </el-dialog>
<el-dialog <el-dialog
v-model="historyVisible" v-model="historyVisible"
title="归属审计记录" title="归属审计记录"
width="800px" width="1100px"
append-to-body append-to-body
class="dialog-form-layout" class="dialog-form-layout"
><el-table :data="historyRows" class="list-table" stripe ><el-table :data="historyRows" class="list-table" stripe
@@ -233,7 +234,7 @@
presignOrderStatusTagType, presignOrderStatusTagType,
applicationStatusTagType applicationStatusTagType
} from '@/utils/status-tag-registry' } from '@/utils/status-tag-registry'
import { Search, Edit, Clock, Close, View, Check, RefreshRight } from '@element-plus/icons-vue' import { Search, Edit, Clock, Close, Check, RefreshRight } from '@element-plus/icons-vue'
import { assignmentTabs, assignmentColumns, assignmentKeywordHints } from './assignment-columns' import { assignmentTabs, assignmentColumns, assignmentKeywordHints } from './assignment-columns'
import ProjectSelect from '@/components/business/ProjectSelect.vue' import ProjectSelect from '@/components/business/ProjectSelect.vue'
import { assignmentSnapshot } from '@/utils/project-management' import { assignmentSnapshot } from '@/utils/project-management'
@@ -268,10 +269,10 @@
loadError = ref(false), loadError = ref(false),
visible = ref(false), visible = ref(false),
busy = ref(false), busy = ref(false),
targetTeamId = ref(''),
target = ref(''), target = ref(''),
reason = ref(''), reason = ref(''),
selected = ref<ProjectRecord>({}), selected = ref<ProjectRecord>({}),
previewRows = ref<ProjectRecord[]>([]),
historyVisible = ref(false), historyVisible = ref(false),
historyRows = ref<ProjectRecord[]>([]) historyRows = ref<ProjectRecord[]>([])
const visibleColumns = computed(() => const visibleColumns = computed(() =>
@@ -361,47 +362,54 @@
} }
function open(row: ProjectRecord) { function open(row: ProjectRecord) {
selected.value = row selected.value = row
targetTeamId.value = ''
target.value = '' target.value = ''
reason.value = '' reason.value = ''
previewRows.value = []
visible.value = true visible.value = true
} }
async function preview() { function onTargetTeamChange() {
target.value = ''
}
async function commit() {
if (!targetTeamId.value) {
ElMessage.warning('请选择新项目组')
return
}
if (!target.value) { if (!target.value) {
ElMessage.warning('请选择目标项目') ElMessage.warning('请选择新项目')
return
}
if (!reason.value.trim()) {
ElMessage.warning('请填写调整原因')
return return
} }
busy.value = true busy.value = true
const previewTarget = target.value const commitTarget = target.value
const previewRecord = selected.value const commitRecord = selected.value
try { try {
const result = await previewAssignment({ const preview = await previewAssignment({
bizType: selected.value.bizType, bizType: selected.value.bizType,
id: selected.value.id, id: selected.value.id,
projectId: target.value projectId: target.value
}) })
if (selected.value === previewRecord && target.value === previewTarget && visible.value) { if (selected.value !== commitRecord || target.value !== commitTarget || !visible.value) {
previewRows.value = result.records return
} }
} finally {
busy.value = false
}
}
async function commit() {
busy.value = true
try {
await saveAssignment({ await saveAssignment({
bizType: selected.value.bizType, bizType: selected.value.bizType,
id: selected.value.id, id: selected.value.id,
projectId: target.value, projectId: target.value,
reason: reason.value.trim(), reason: reason.value.trim(),
expectedAssignments: assignmentSnapshot(previewRows.value) expectedAssignments: assignmentSnapshot(preview.records)
}) })
if (selected.value !== commitRecord || target.value !== commitTarget || !visible.value) {
return
}
visible.value = false visible.value = false
ElMessage.success('归属已调整') ElMessage.success('归属已调整')
await load() await load()
} catch { } catch {
previewRows.value = [] // API layer already surfaces the conflict / validation message.
} finally { } finally {
busy.value = false busy.value = false
} }