Files
zkxw/mobile-oa-h5/src/market/components/ProjectAssignmentField.vue
T
zlwuandCursor fefef41524 fix(mobile): 协议单表单将项目归属移入协议信息分区
按专员/经理协议单诉求,把项目归属嵌入协议信息卡片并置于协议状态上方,避免独立成卡打断填写顺序。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-11-05 17:57:57 +08:00

103 lines
4.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<component :is="embedded ? 'div' : 'section'" :class="rootClass">
<h2 v-if="!embedded">{{ filter ? '项目筛选' : '项目归属' }}<span v-if="required && !editing && !filter"> *</span></h2>
<label v-else :id="labelId">所属项目<b v-if="required && !editing && !filter">*</b></label>
<template v-if="editing">
<template v-if="embedded">
<div class="market-form-field__control market-form-field__control--text" :aria-labelledby="labelId">
{{ projectLabel(record) }}
</div>
<p class="market-form-field__tip">项目组:{{ record.teamName || '—' }};归属调整请使用 Web 项目归属维护</p>
</template>
<template v-else>
<van-cell title="所属项目" :value="projectLabel(record)" />
<van-cell title="项目组" :value="record.teamName || '—'" />
<van-cell label="归属调整请使用 Web 项目归属维护" />
</template>
</template>
<template v-else>
<div :class="embedded ? 'market-form-field__control' : undefined">
<PresignSelectField
:model-value="modelValue || ''"
:options="selectOptions"
:disabled="disabled || loading || Boolean(error)"
:loading="loading"
searchable
title="所属项目"
:placeholder="filter ? '全部项目' : '请选择所属项目'"
@update:model-value="emit('update:modelValue', $event)"
/>
</div>
<template v-if="embedded">
<p v-if="loading" class="market-form-field__tip">正在加载项目…</p>
<button v-else-if="error" type="button" class="market-form-field__tip market-form-field__tip--action" @click="load">
项目加载失败,点击重试({{ error }})
</button>
<p v-else-if="!options.length" class="market-form-field__tip">
{{ required && !filter ? '请联系管理员配置可选项目后再保存' : '当前没有可选项目' }}
</p>
</template>
<template v-else>
<van-cell v-if="loading" title="正在加载项目…" />
<van-cell v-else-if="error" :label="error" title="项目加载失败,点击重试" is-link @click="load" />
<van-cell v-else-if="!options.length" :label="required && !filter ? '请联系管理员配置可选项目后再保存' : '当前没有可选项目'" />
</template>
</template>
</component>
</template>
<script setup>
import { computed, onBeforeUnmount, ref, watch } from 'vue'
import { fetchProjectOptions } from '../../api/market.js'
import { projectLabel, projectOptions, projectValidation } from '../project-assignment.js'
import PresignSelectField from './PresignSelectField.vue'
const props = defineProps({
modelValue: { type: String, default: '' },
bizType: { type: String, required: true },
editing: { type: Boolean, default: false },
filter: { type: Boolean, default: false },
embedded: { type: Boolean, default: false },
context: { type: Object, default: null },
disabled: { type: Boolean, default: false },
record: { type: Object, default: () => ({}) }
})
const emit = defineEmits(['update:modelValue'])
const options = ref([])
const required = ref(false)
const loading = ref(true)
const error = ref('')
const labelId = `project-assignment-${Math.random().toString(36).slice(2, 9)}`
let controller
const rootClass = computed(() => (props.embedded ? 'market-form-field' : 'market-form-section'))
const selectOptions = computed(() => [
...(!required.value || props.filter ? [{ text: props.filter ? '全部项目' : '暂不归属', value: '' }] : []),
...options.value
])
async function load() {
controller?.abort()
if (props.editing) { loading.value = false; return }
const current = new AbortController()
controller = current
loading.value = true
error.value = ''
options.value = []
try {
const data = await fetchProjectOptions(props.bizType, { signal: current.signal, filter: props.filter, context: props.context })
if (controller !== current) return
options.value = projectOptions(data)
required.value = Boolean(data.required)
} catch (cause) {
if (controller === current && cause?.name !== 'AbortError') error.value = cause?.message || '请稍后重试'
} finally {
if (controller === current) loading.value = false
}
}
watch(() => [props.bizType, props.editing], (next, previous) => {
if (previous && next[0] !== previous[0] && !props.editing) emit('update:modelValue', '')
load()
}, { immediate: true })
onBeforeUnmount(() => { controller?.abort(); controller = null })
defineExpose({ validate: () => projectValidation({ editing: props.editing || props.filter, loading: loading.value, error: error.value, required: required.value, projectId: props.modelValue }) })
</script>