2026-02-02 15:15:51 +08:00
|
|
|
|
<template>
|
2026-01-20 16:24:43 +08:00
|
|
|
|
<view class="transfer-container">
|
|
|
|
|
|
<view class="content">
|
2026-02-06 15:38:54 +08:00
|
|
|
|
<view class="tips">处理中...</view>
|
2026-01-20 16:24:43 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2026-02-06 15:38:54 +08:00
|
|
|
|
import { ref } from 'vue';
|
2026-01-23 17:59:24 +08:00
|
|
|
|
import { onLoad } from '@dcloudio/uni-app';
|
|
|
|
|
|
import { storeToRefs } from 'pinia';
|
|
|
|
|
|
import api from '@/utils/api';
|
|
|
|
|
|
import useAccountStore from '@/store/account';
|
2026-02-06 15:38:54 +08:00
|
|
|
|
import { confirm as uniConfirm, hideLoading, loading as showLoading, toast } from '@/utils/widget';
|
2026-01-20 16:24:43 +08:00
|
|
|
|
|
2026-01-23 17:59:24 +08:00
|
|
|
|
const CURRENT_TEAM_STORAGE_KEY = 'ykt_case_current_team';
|
|
|
|
|
|
const NEED_RELOAD_STORAGE_KEY = 'ykt_case_need_reload';
|
|
|
|
|
|
const BATCH_CUSTOMER_IDS_KEY = 'ykt_case_batch_customer_ids';
|
|
|
|
|
|
|
|
|
|
|
|
const accountStore = useAccountStore();
|
|
|
|
|
|
const { account, doctorInfo } = storeToRefs(accountStore);
|
|
|
|
|
|
const { getDoctorInfo } = accountStore;
|
|
|
|
|
|
|
|
|
|
|
|
const customerIds = ref([]);
|
|
|
|
|
|
const currentTeam = ref(null);
|
|
|
|
|
|
const teams = ref([]);
|
|
|
|
|
|
|
|
|
|
|
|
function getUserId() {
|
|
|
|
|
|
const d = doctorInfo.value || {};
|
|
|
|
|
|
const a = account.value || {};
|
|
|
|
|
|
return String(d.userid || d.userId || d.corpUserId || a.userid || a.userId || '') || '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getCorpId() {
|
|
|
|
|
|
const t = currentTeam.value || {};
|
|
|
|
|
|
const a = account.value || {};
|
|
|
|
|
|
const d = doctorInfo.value || {};
|
|
|
|
|
|
return String(t.corpId || d.corpId || a.corpId || '') || '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getCurrentTeamId() {
|
|
|
|
|
|
return String(currentTeam.value?.teamId || '') || '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
function showActionSheet(itemList = [], title = '') {
|
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
|
if (!Array.isArray(itemList) || itemList.length === 0) {
|
|
|
|
|
|
reject(new Error('empty'));
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
uni.showActionSheet({
|
|
|
|
|
|
title,
|
|
|
|
|
|
itemList,
|
|
|
|
|
|
success: ({ tapIndex }) => resolve(tapIndex),
|
|
|
|
|
|
fail: () => reject(new Error('cancel')),
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 17:59:24 +08:00
|
|
|
|
function normalizeTeam(raw) {
|
|
|
|
|
|
if (!raw || typeof raw !== 'object') return null;
|
|
|
|
|
|
const teamId = raw.teamId || raw.id || raw._id || '';
|
|
|
|
|
|
const name = raw.name || raw.teamName || raw.team || '';
|
|
|
|
|
|
const corpId = raw.corpId || raw.corpID || '';
|
|
|
|
|
|
if (!teamId || !name) return null;
|
|
|
|
|
|
return { teamId: String(teamId), name: String(name), corpId: corpId ? String(corpId) : '' };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function ensureDoctor() {
|
|
|
|
|
|
if (doctorInfo.value) return;
|
|
|
|
|
|
if (!account.value?.openid) return;
|
|
|
|
|
|
try {
|
|
|
|
|
|
await getDoctorInfo();
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
// ignore
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function loadTeams() {
|
|
|
|
|
|
await ensureDoctor();
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
const userId = getUserId();
|
2026-02-06 15:38:54 +08:00
|
|
|
|
if (!corpId || !userId) return [];
|
2026-01-23 17:59:24 +08:00
|
|
|
|
const res = await api('getTeamBymember', { corpId, corpUserId: userId });
|
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
|
toast(res?.message || '获取团队失败');
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return [];
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
const list = Array.isArray(res?.data) ? res.data : Array.isArray(res?.data?.data) ? res.data.data : [];
|
|
|
|
|
|
teams.value = list.map(normalizeTeam).filter(Boolean);
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return teams.value;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
async function fetchTeamMembers(teamId) {
|
2026-01-23 17:59:24 +08:00
|
|
|
|
const corpId = getCorpId();
|
2026-02-06 15:38:54 +08:00
|
|
|
|
if (!teamId) return [];
|
2026-01-23 17:59:24 +08:00
|
|
|
|
const res = await api('getTeamData', { corpId, teamId });
|
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
|
toast(res?.message || '获取团队成员失败');
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return [];
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
const t = res?.data && typeof res.data === 'object' ? res.data : {};
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return Array.isArray(t.memberList) ? t.memberList : [];
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
async function transferToCustomerPool() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
await uniConfirm('客户将与本团队解除服务关系,本团队成员将没有权限查询到客户档案。');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
const currentTeamId = getCurrentTeamId();
|
|
|
|
|
|
const creatorUserId = getUserId();
|
|
|
|
|
|
if (!corpId || !currentTeamId || !creatorUserId) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
showLoading('保存中...');
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await api('transferCustomers', {
|
|
|
|
|
|
corpId,
|
|
|
|
|
|
customerIds: customerIds.value,
|
|
|
|
|
|
currentTeamId,
|
|
|
|
|
|
operationType: 'transferToCustomerPool',
|
|
|
|
|
|
creatorUserId,
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
|
toast(res?.message || '操作失败');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
toast('操作成功');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
toast('操作失败');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
hideLoading();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function transferToOtherTeam() {
|
|
|
|
|
|
const corpId = getCorpId();
|
|
|
|
|
|
const currentTeamId = getCurrentTeamId();
|
|
|
|
|
|
const creatorUserId = getUserId();
|
|
|
|
|
|
if (!corpId || !currentTeamId || !creatorUserId) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-23 17:59:24 +08:00
|
|
|
|
if (!teams.value.length) await loadTeams();
|
2026-02-06 15:38:54 +08:00
|
|
|
|
const candidates = teams.value.filter((t) => String(t?.teamId || '') !== String(currentTeamId));
|
2026-01-23 17:59:24 +08:00
|
|
|
|
if (!candidates.length) {
|
|
|
|
|
|
toast('暂无可选团队');
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return false;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
2026-01-20 16:24:43 +08:00
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
let teamIndex;
|
|
|
|
|
|
try {
|
|
|
|
|
|
teamIndex = await showActionSheet(candidates.map((t) => t.name || ''), '选择新负责团队');
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
const pickedTeam = candidates[teamIndex];
|
|
|
|
|
|
if (!pickedTeam?.teamId) {
|
|
|
|
|
|
toast('团队信息异常');
|
|
|
|
|
|
return false;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
2026-01-20 16:24:43 +08:00
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
const members = await fetchTeamMembers(pickedTeam.teamId);
|
|
|
|
|
|
if (!members.length) {
|
|
|
|
|
|
toast('当前团队暂无可选成员');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-01-20 16:24:43 +08:00
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
let userIndex;
|
|
|
|
|
|
try {
|
|
|
|
|
|
userIndex = await showActionSheet(
|
|
|
|
|
|
members.map((m) => String(m?.anotherName || m?.name || m?.userid || '')),
|
|
|
|
|
|
'选择责任人'
|
|
|
|
|
|
);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
const targetUserId = String(members[userIndex]?.userid || '') || '';
|
|
|
|
|
|
if (!targetUserId) {
|
|
|
|
|
|
toast('责任人信息异常');
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
showLoading('保存中...');
|
2026-01-23 17:59:24 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const res = await api('transferCustomers', {
|
|
|
|
|
|
corpId,
|
|
|
|
|
|
customerIds: customerIds.value,
|
|
|
|
|
|
currentTeamId,
|
2026-02-06 15:38:54 +08:00
|
|
|
|
targetTeamId: String(pickedTeam.teamId),
|
|
|
|
|
|
targetUserId,
|
2026-01-23 17:59:24 +08:00
|
|
|
|
operationType: 'transferToOtherTeam',
|
|
|
|
|
|
creatorUserId,
|
|
|
|
|
|
});
|
|
|
|
|
|
if (!res?.success) {
|
|
|
|
|
|
toast(res?.message || '操作失败');
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return false;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
toast('操作成功');
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return true;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
} catch (e) {
|
|
|
|
|
|
toast('操作失败');
|
2026-02-06 15:38:54 +08:00
|
|
|
|
return false;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
hideLoading();
|
2026-01-20 16:24:43 +08:00
|
|
|
|
}
|
2026-02-06 15:38:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async function runFlow(options = {}) {
|
|
|
|
|
|
let mode = options?.mode === 'pool' ? 'pool' : options?.mode === 'team' ? 'team' : '';
|
|
|
|
|
|
if (!mode) {
|
|
|
|
|
|
let pick;
|
|
|
|
|
|
try {
|
|
|
|
|
|
pick = await showActionSheet(['转移给其他团队', '转移至客户公共池']);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
mode = pick === 0 ? 'team' : 'pool';
|
|
|
|
|
|
}
|
|
|
|
|
|
return mode === 'team' ? await transferToOtherTeam() : await transferToCustomerPool();
|
|
|
|
|
|
}
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-02-06 15:38:54 +08:00
|
|
|
|
onLoad(async (options = {}) => {
|
2026-01-23 17:59:24 +08:00
|
|
|
|
customerIds.value = Array.isArray(uni.getStorageSync(BATCH_CUSTOMER_IDS_KEY))
|
|
|
|
|
|
? uni.getStorageSync(BATCH_CUSTOMER_IDS_KEY).map(String).filter(Boolean)
|
|
|
|
|
|
: [];
|
|
|
|
|
|
currentTeam.value = uni.getStorageSync(CURRENT_TEAM_STORAGE_KEY) || null;
|
2026-02-06 15:38:54 +08:00
|
|
|
|
|
2026-01-23 17:59:24 +08:00
|
|
|
|
if (!customerIds.value.length) {
|
|
|
|
|
|
toast('未选择客户');
|
|
|
|
|
|
setTimeout(() => uni.navigateBack(), 200);
|
2026-01-20 16:24:43 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-02-06 15:38:54 +08:00
|
|
|
|
|
|
|
|
|
|
const ok = await runFlow(options);
|
|
|
|
|
|
if (ok) {
|
|
|
|
|
|
uni.removeStorageSync(BATCH_CUSTOMER_IDS_KEY);
|
|
|
|
|
|
uni.setStorageSync(NEED_RELOAD_STORAGE_KEY, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
setTimeout(() => uni.navigateBack(), 50);
|
2026-01-23 17:59:24 +08:00
|
|
|
|
});
|
2026-01-20 16:24:43 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
.transfer-container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
background-color: #f7f8fa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.content {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
margin-bottom: 10px;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-01-20 16:24:43 +08:00
|
|
|
|
&:first-child {
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.selector-item {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
height: 44px;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 0 15px;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-01-20 16:24:43 +08:00
|
|
|
|
text {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #333;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-01-20 16:24:43 +08:00
|
|
|
|
&.placeholder {
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.tips {
|
|
|
|
|
|
margin-top: 15px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.footer {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
padding: 15px 20px 30px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 15px;
|
2026-01-23 17:59:24 +08:00
|
|
|
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
2026-01-20 16:24:43 +08:00
|
|
|
|
|
|
|
|
|
|
.btn {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
height: 44px;
|
|
|
|
|
|
line-height: 44px;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
|
|
|
|
|
|
&.plain {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
color: #666;
|
|
|
|
|
|
border: 1px solid #ddd;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.primary {
|
2026-02-02 15:15:51 +08:00
|
|
|
|
background-color: #0877F1;
|
2026-01-20 16:24:43 +08:00
|
|
|
|
color: #fff;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|
2026-01-20 16:24:43 +08:00
|
|
|
|
&::after {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
2026-01-23 17:59:24 +08:00
|
|
|
|
|