135 lines
4.9 KiB
Vue
135 lines
4.9 KiB
Vue
<script setup>
|
|
import { ref } from 'vue'
|
|
import { onLoad, onShow } from '@dcloudio/uni-app'
|
|
import { getOpportunity, listOpportunityFollow, addOpportunityFollow } from '@/api/crm'
|
|
import { labelOf, dash, formatMoney } from '@/utils/crmLabels'
|
|
import { PAGE_PATHS } from '@/config/api'
|
|
|
|
const id = ref('')
|
|
const detail = ref(null)
|
|
const follows = ref([])
|
|
const showFollow = ref(false)
|
|
const followContent = ref('')
|
|
const followType = ref('电话')
|
|
const submitting = ref(false)
|
|
|
|
const stageLabel = (s) => labelOf('opportunityStage', s)
|
|
const statusLabel = (s) => labelOf('opportunityStatus', s)
|
|
|
|
const goBack = () => {
|
|
uni.navigateBack({
|
|
fail: () => uni.navigateTo({ url: PAGE_PATHS.crmOpportunityList })
|
|
})
|
|
}
|
|
|
|
const load = async () => {
|
|
if (!id.value) return
|
|
try {
|
|
detail.value = await getOpportunity(id.value)
|
|
const fl = await listOpportunityFollow(id.value, 1, 50)
|
|
follows.value = fl.rows || []
|
|
} catch (e) {
|
|
uni.showToast({ title: e.message || '加载失败', icon: 'none' })
|
|
}
|
|
}
|
|
|
|
const submitFollow = async () => {
|
|
if (!followContent.value.trim()) {
|
|
uni.showToast({ title: '请填写跟进内容', icon: 'none' })
|
|
return
|
|
}
|
|
submitting.value = true
|
|
try {
|
|
await addOpportunityFollow({
|
|
opportunityId: id.value,
|
|
followType: followType.value,
|
|
followContent: followContent.value.trim()
|
|
})
|
|
uni.showToast({ title: '已保存', icon: 'success' })
|
|
showFollow.value = false
|
|
followContent.value = ''
|
|
await load()
|
|
} catch (e) {
|
|
uni.showToast({ title: e.message || '保存失败', icon: 'none' })
|
|
} finally {
|
|
submitting.value = false
|
|
}
|
|
}
|
|
|
|
onLoad((q) => {
|
|
id.value = q?.id || ''
|
|
})
|
|
onShow(() => load())
|
|
</script>
|
|
|
|
<template>
|
|
<view class="mp-page mp-page--sub mp-page--with-bottom">
|
|
<view class="mp-header">
|
|
<text class="mp-subpage-back" @click="goBack">← 返回</text>
|
|
<text class="mp-header__title" style="margin-top: 16rpx">商机详情</text>
|
|
</view>
|
|
|
|
<view v-if="detail" class="mp-detail-hero">
|
|
<view class="mp-detail-hero__top">
|
|
<view>
|
|
<text class="mp-detail-hero__title">{{ dash(detail.opportunityName) }}</text>
|
|
<text class="mp-detail-hero__sub">{{ dash(detail.customerName) }}</text>
|
|
</view>
|
|
<text class="mp-tag" :class="'mp-tag--' + statusLabel(detail.status).tag">
|
|
{{ statusLabel(detail.status).text }}
|
|
</text>
|
|
</view>
|
|
<view class="mp-detail-hero__meta">
|
|
<text class="mp-detail-hero__chip">{{ stageLabel(detail.stage).text }}</text>
|
|
<text class="mp-detail-hero__chip">{{ formatMoney(detail.amount ?? detail.expectedAmount) }}</text>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="mp-block">
|
|
<text class="mp-block__title">基本信息</text>
|
|
<view class="mp-block__panel">
|
|
<view class="mp-kv"><text class="mp-kv__label">商机编号</text><text class="mp-kv__value">{{ dash(detail?.opportunityNo) }}</text></view>
|
|
<view class="mp-kv"><text class="mp-kv__label">负责人</text><text class="mp-kv__value">{{ dash(detail?.ownerUserName) }}</text></view>
|
|
<view class="mp-kv"><text class="mp-kv__label">来源</text><text class="mp-kv__value">{{ dash(detail?.source) }}</text></view>
|
|
<view class="mp-kv"><text class="mp-kv__label">预计成交</text><text class="mp-kv__value">{{ dash(detail?.expectedDate || detail?.dealDate) }}</text></view>
|
|
<view class="mp-kv"><text class="mp-kv__label">备注</text><text class="mp-kv__value">{{ dash(detail?.remark) }}</text></view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="mp-block">
|
|
<text class="mp-block__title">跟进记录</text>
|
|
<mp-empty v-if="!follows.length" compact title="暂无跟进" />
|
|
<view v-else class="mp-list">
|
|
<view v-for="f in follows" :key="f.id" class="mp-card">
|
|
<view class="mp-card__head">
|
|
<text class="mp-card__title">{{ dash(f.followType) }}</text>
|
|
<text class="mp-card__sub">{{ dash(f.createTime) }}</text>
|
|
</view>
|
|
<text style="font-size: 26rpx">{{ dash(f.followContent) }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="showFollow" class="mp-form-panel">
|
|
<view class="mp-form-row">
|
|
<text class="mp-form-label">跟进方式</text>
|
|
<input v-model="followType" class="mp-form-input" />
|
|
</view>
|
|
<view class="mp-form-row">
|
|
<text class="mp-form-label mp-form-label--req">跟进内容</text>
|
|
<textarea v-model="followContent" class="mp-form-textarea" />
|
|
</view>
|
|
<view class="mp-form-row" style="display: flex; gap: 16rpx">
|
|
<view class="mp-btn mp-btn--ghost" style="flex: 1" @click="showFollow = false">取消</view>
|
|
<view class="mp-btn mp-btn--primary" style="flex: 1" @click="submitFollow">
|
|
{{ submitting ? '提交中…' : '提交' }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<view class="mp-bottom-bar">
|
|
<view class="mp-btn mp-btn--primary" @click="showFollow = true">写跟进</view>
|
|
</view>
|
|
</view>
|
|
</template>
|