842 lines
23 KiB
TypeScript
842 lines
23 KiB
TypeScript
import { UserInfo } from "@/components/UserInfo";
|
||
import { API_CONFIG } from "@/config/api";
|
||
import httpService, { ApiResponse } from "./httpService";
|
||
import uploadFiles from "./uploadFiles";
|
||
import Taro from "@tarojs/taro";
|
||
import getCurrentConfig from "@/config/env";
|
||
import { clear_login_state } from "@/services/loginService";
|
||
|
||
// 用户详情接口
|
||
interface UserDetailData {
|
||
id: number;
|
||
openid: string;
|
||
user_code: string | null;
|
||
unionid: string;
|
||
session_key: string;
|
||
nickname: string;
|
||
avatar_url: string;
|
||
gender: string;
|
||
country: string;
|
||
province: string;
|
||
city: string;
|
||
district: string;
|
||
language: string;
|
||
phone: string;
|
||
is_subscribed: string;
|
||
latitude: string;
|
||
longitude: string;
|
||
subscribe_time: string;
|
||
last_login_time: string;
|
||
create_time: string;
|
||
last_modify_time: string;
|
||
personal_profile: string;
|
||
occupation: string;
|
||
birthday: string;
|
||
ntrp_level: string;
|
||
stats: {
|
||
followers_count: number;
|
||
following_count: number;
|
||
hosted_games_count: number;
|
||
participated_games_count: number;
|
||
};
|
||
}
|
||
|
||
export interface PickerOption {
|
||
text: string | number;
|
||
value: string | number;
|
||
children?: PickerOption[];
|
||
}
|
||
|
||
export interface Profession {
|
||
name: string;
|
||
children: Profession[] | [];
|
||
}
|
||
|
||
// 用户详细信息接口(从 loginService 移过来)
|
||
export interface UserInfoType {
|
||
id: number;
|
||
openid: string;
|
||
unionid: string;
|
||
session_key: string;
|
||
nickname: string;
|
||
avatar_url: string;
|
||
gender: string;
|
||
country: string;
|
||
province: string;
|
||
city: string;
|
||
district: string;
|
||
language: string;
|
||
phone: string;
|
||
is_subscribed: string;
|
||
latitude: number;
|
||
longitude: number;
|
||
subscribe_time: string;
|
||
last_login_time: string;
|
||
avatar: string;
|
||
join_date: string;
|
||
stats: {
|
||
following_count: number;
|
||
followers_count: number;
|
||
hosted_games_count: number;
|
||
participated_games_count: number;
|
||
};
|
||
personal_profile: string;
|
||
occupation: string;
|
||
ntrp_level: string;
|
||
bio?: string;
|
||
birthday?: string;
|
||
is_following?: boolean;
|
||
tags?: string[];
|
||
ongoing_games?: string[];
|
||
}
|
||
|
||
export interface NicknameChangeStatus {
|
||
can_change: boolean;
|
||
remaining_count: number;
|
||
period_start_time: string;
|
||
next_period_start_time: string;
|
||
days_until_next_period: number;
|
||
}
|
||
|
||
// 后端球局数据接口
|
||
interface BackendGameData {
|
||
id: number;
|
||
title: string;
|
||
description: string;
|
||
game_type?: string;
|
||
play_type: string;
|
||
publisher_id?: string;
|
||
venue_id?: string;
|
||
max_players?: number;
|
||
current_players?: number;
|
||
price: string;
|
||
price_mode: string;
|
||
court_type: string;
|
||
court_surface: string;
|
||
gender_limit?: string;
|
||
skill_level_min: string;
|
||
skill_level_max: string;
|
||
start_time: string;
|
||
end_time: string;
|
||
location_name: string | null;
|
||
location: string;
|
||
latitude?: string;
|
||
longitude?: string;
|
||
image_list?: string[];
|
||
description_tag?: string[];
|
||
venue_description_tag?: string[];
|
||
venue_image_list?: Array<{ id: string; url: string }>;
|
||
participant_count: number;
|
||
max_participants: number;
|
||
participant_info?: {
|
||
id: number;
|
||
status: string;
|
||
payment_status: string;
|
||
joined_at: string;
|
||
deposit_amount: number;
|
||
join_message: string;
|
||
skill_level: string;
|
||
contact_info: string;
|
||
};
|
||
venue_dtl?: {
|
||
id: number;
|
||
name: string;
|
||
address: string;
|
||
latitude: string;
|
||
longitude: string;
|
||
venue_type: string;
|
||
surface_type: string;
|
||
};
|
||
participants: {
|
||
user: {
|
||
avatar_url: string;
|
||
};
|
||
}[];
|
||
}
|
||
|
||
const formatOptions = (data: Profession[]): PickerOption[] => {
|
||
return data.map((item: Profession) => {
|
||
const { name: text, children } = item;
|
||
const itm: PickerOption = {
|
||
text,
|
||
value: text,
|
||
children: children ? formatOptions(children) : [],
|
||
};
|
||
if (!itm.children!.length) {
|
||
delete itm.children;
|
||
}
|
||
return itm;
|
||
});
|
||
};
|
||
|
||
// 用户服务类
|
||
export class UserService {
|
||
// 数据转换函数:将后端数据转换为ListContainer期望的格式
|
||
private static transform_game_data(backend_data: BackendGameData[]): any[] {
|
||
return backend_data.map((game) => {
|
||
// 处理时间格式
|
||
const start_time = new Date(game.start_time.replace(/\s/, "T"));
|
||
const date_time = this.format_date_time(start_time);
|
||
|
||
// 处理图片数组 - 兼容两种数据格式
|
||
let images: string[] = [];
|
||
if (game.image_list && game.image_list.length > 0) {
|
||
images = game.image_list.filter((img) => img && img.trim() !== "");
|
||
} else if (game.venue_image_list && game.venue_image_list.length > 0) {
|
||
images = game.venue_image_list
|
||
.filter((img) => img && img.url && img.url.trim() !== "")
|
||
.map((img) => img.url);
|
||
}
|
||
|
||
// 处理距离 - 优先使用venue_dtl中的坐标,其次使用game中的坐标
|
||
let latitude: number =
|
||
typeof game.latitude === "number"
|
||
? game.latitude
|
||
: parseFloat(game.latitude || "0") || 0;
|
||
let longitude: number =
|
||
typeof game.longitude === "number"
|
||
? game.longitude
|
||
: parseFloat(game.longitude || "0") || 0;
|
||
if (game.venue_dtl) {
|
||
latitude = parseFloat(game.venue_dtl.latitude) || latitude;
|
||
longitude = parseFloat(game.venue_dtl.longitude) || longitude;
|
||
}
|
||
const distance = this.calculate_distance(latitude, longitude);
|
||
|
||
// 处理地点信息 - 优先使用venue_dtl中的信息
|
||
let location = game.location_name || game.location || "未知地点";
|
||
if (game.venue_dtl && game.venue_dtl.name) {
|
||
location = game.venue_dtl.name;
|
||
}
|
||
|
||
// 处理人数统计 - 兼容不同的字段名
|
||
const registered_count =
|
||
game.current_players || game.participant_count || 0;
|
||
const max_count = game.max_players || game.max_participants || 0;
|
||
|
||
// 转换为 ListCard 期望的格式
|
||
return {
|
||
id: game.id,
|
||
title: game.title || "未命名球局",
|
||
start_time: date_time,
|
||
original_start_time: game.start_time,
|
||
end_time: game.end_time || "",
|
||
location: location,
|
||
distance_km: parseFloat(distance.replace("km", "")) || 0,
|
||
current_players: registered_count,
|
||
max_players: max_count,
|
||
skill_level_min: parseInt(game.skill_level_min) || 0,
|
||
skill_level_max: parseInt(game.skill_level_max) || 0,
|
||
play_type: game.play_type || "不限",
|
||
image_list: images,
|
||
court_type: game.court_type || "未知",
|
||
matchType: game.play_type || "不限",
|
||
shinei: game.court_type || "未知",
|
||
participants: game.participants || [],
|
||
};
|
||
});
|
||
}
|
||
|
||
private static is_date_in_this_week(date: Date): boolean {
|
||
const today = new Date();
|
||
const firstDayOfWeek = new Date(
|
||
today.setDate(today.getDate() - today.getDay())
|
||
);
|
||
const lastDayOfWeek = new Date(
|
||
firstDayOfWeek.setDate(firstDayOfWeek.getDate() + 6)
|
||
);
|
||
|
||
return date >= firstDayOfWeek && date <= lastDayOfWeek;
|
||
}
|
||
|
||
// 格式化时间显示
|
||
private static format_date_time(start_time: Date): string {
|
||
const now = new Date();
|
||
const today = new Date(
|
||
now.getFullYear(),
|
||
now.getMonth() + 1,
|
||
now.getDate()
|
||
);
|
||
const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000);
|
||
const day_after_tomorrow = new Date(
|
||
today.getTime() + 2 * 24 * 60 * 60 * 1000
|
||
);
|
||
const weekdays = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"];
|
||
|
||
const start_date = new Date(
|
||
start_time.getFullYear(),
|
||
start_time.getMonth() + 1,
|
||
start_time.getDate()
|
||
);
|
||
const weekday = weekdays[start_time.getDay()];
|
||
|
||
let date_str = "";
|
||
if (start_date.getTime() === today.getTime()) {
|
||
date_str = "今天";
|
||
} else if (start_date.getTime() === tomorrow.getTime()) {
|
||
date_str = `明天(${weekday})`;
|
||
} else if (start_date.getTime() === day_after_tomorrow.getTime()) {
|
||
date_str = `后天(${weekday})`;
|
||
} else if (this.is_date_in_this_week(start_time)) {
|
||
date_str = weekday;
|
||
} else {
|
||
date_str = `${start_time.getFullYear()}-${(start_time.getMonth() + 1)
|
||
.toString()
|
||
.padStart(2, "0")}-${start_time
|
||
.getDate()
|
||
.toString()
|
||
.padStart(2, "0")}(${weekday})`;
|
||
}
|
||
|
||
const time_str = `${start_time
|
||
.getHours()
|
||
.toString()
|
||
.padStart(2, "0")}:${start_time
|
||
.getMinutes()
|
||
.toString()
|
||
.padStart(2, "0")}`;
|
||
return `${date_str} ${time_str}`;
|
||
}
|
||
|
||
// 计算距离(模拟实现,实际需要根据用户位置计算)
|
||
private static calculate_distance(
|
||
latitude: number,
|
||
longitude: number
|
||
): string {
|
||
if (latitude === 0 && longitude === 0) {
|
||
return "未知距离";
|
||
}
|
||
|
||
// 这里应该根据用户当前位置计算实际距离
|
||
// 暂时返回模拟距离
|
||
const distances = ["1.2km", "2.5km", "3.8km", "5.1km", "7.3km"];
|
||
return distances[Math.floor(Math.random() * distances.length)];
|
||
}
|
||
// 获取用户信息
|
||
static async get_user_info(user_id?: string): Promise<UserInfo> {
|
||
try {
|
||
const response = await httpService.post<UserDetailData>(
|
||
API_CONFIG.USER.DETAIL,
|
||
user_id ? { user_id } : {},
|
||
{
|
||
showLoading: false,
|
||
}
|
||
);
|
||
|
||
if (response.code === 0) {
|
||
const userData = response.data;
|
||
return {
|
||
id: userData.id || "",
|
||
nickname: userData.nickname || "",
|
||
avatar: userData.avatar_url || "",
|
||
join_date: userData.subscribe_time
|
||
? `${new Date(userData.subscribe_time).getFullYear()}年${
|
||
new Date(userData.subscribe_time).getMonth() + 1
|
||
}月加入`
|
||
: "",
|
||
stats: {
|
||
following: userData.stats?.following_count || 0,
|
||
friends: userData.stats?.followers_count || 0,
|
||
hosted: userData.stats?.hosted_games_count || 0,
|
||
participated: userData.stats?.participated_games_count || 0,
|
||
},
|
||
|
||
personal_profile: userData.personal_profile || "",
|
||
occupation: userData.occupation || "",
|
||
ntrp_level: userData.ntrp_level || "",
|
||
phone: userData.phone || "",
|
||
gender: userData.gender || "",
|
||
birthday: userData.birthday || "",
|
||
country: userData.country || "",
|
||
province: userData.province || "",
|
||
city: userData.city || "",
|
||
};
|
||
} else {
|
||
throw new Error(response.message || "获取用户信息失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取用户信息失败:", error);
|
||
// 返回默认用户信息
|
||
return {} as UserInfo;
|
||
}
|
||
}
|
||
|
||
// 更新用户信息(简化版本,具体逻辑在 userStore 中处理)
|
||
static async update_user_info(update_data: Partial<UserInfo>): Promise<void> {
|
||
try {
|
||
// 过滤掉空字段
|
||
const filtered_data: Record<string, any> = {};
|
||
|
||
Object.keys(update_data).forEach((key) => {
|
||
const value = update_data[key as keyof UserInfo];
|
||
// 只添加非空且非空字符串的字段
|
||
if (value !== null && value !== undefined && value !== "") {
|
||
if (typeof value === "string" && value.trim() !== "") {
|
||
filtered_data[key] = value.trim();
|
||
} else if (typeof value !== "string") {
|
||
filtered_data[key] = value;
|
||
}
|
||
}
|
||
});
|
||
// 如果没有需要更新的字段,直接返回
|
||
if (Object.keys(filtered_data).length === 0) {
|
||
console.log("没有需要更新的字段");
|
||
return;
|
||
}
|
||
|
||
const response = await httpService.post(
|
||
API_CONFIG.USER.UPDATE,
|
||
filtered_data,
|
||
{
|
||
showLoading: true,
|
||
}
|
||
);
|
||
|
||
if (response.code !== 0) {
|
||
throw new Error(response.message || "更新用户信息失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("更新用户信息失败:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取用户主办的球局
|
||
static async get_hosted_games(userId: string | number): Promise<any[]> {
|
||
try {
|
||
const response = await httpService.post<any>(
|
||
API_CONFIG.USER.HOSTED_GAMES,
|
||
{
|
||
userId,
|
||
},
|
||
{
|
||
showLoading: false,
|
||
}
|
||
);
|
||
|
||
if (response.code === 0) {
|
||
// 使用数据转换函数将后端数据转换为ListContainer期望的格式
|
||
return this.transform_game_data(response.data.rows || []);
|
||
} else {
|
||
throw new Error(response.message || "获取主办球局失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取主办球局失败:", error);
|
||
// 返回符合ListContainer data格式的模拟数据
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 获取用户参与的球局
|
||
static async get_participated_games(userId: string | number): Promise<any[]> {
|
||
try {
|
||
const response = await httpService.post<any>(
|
||
API_CONFIG.USER.PARTICIPATED_GAMES,
|
||
{
|
||
userId,
|
||
},
|
||
{
|
||
showLoading: false,
|
||
}
|
||
);
|
||
|
||
if (response.code === 0) {
|
||
// 使用数据转换函数将后端数据转换为ListContainer期望的格式
|
||
return this.transform_game_data(response.data.rows || []);
|
||
} else {
|
||
throw new Error(response.message || "获取参与球局失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取参与球局失败:", error);
|
||
// 返回符合ListContainer data格式的模拟数据
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 获取用户球局记录(兼容旧方法)
|
||
static async get_user_games(
|
||
user_id: string | number,
|
||
type: "hosted" | "participated"
|
||
): Promise<any[]> {
|
||
if (type === "hosted") {
|
||
return this.get_hosted_games(user_id);
|
||
} else {
|
||
return this.get_participated_games(user_id);
|
||
}
|
||
}
|
||
|
||
// 关注/取消关注用户
|
||
static async toggle_follow(
|
||
following_id: string | number,
|
||
is_following: boolean
|
||
): Promise<boolean> {
|
||
try {
|
||
const endpoint = is_following
|
||
? API_CONFIG.USER.UNFOLLOW
|
||
: API_CONFIG.USER.FOLLOW;
|
||
const response = await httpService.post<any>(
|
||
endpoint,
|
||
{ following_id },
|
||
{
|
||
showLoading: true,
|
||
loadingText: is_following ? "取消关注中..." : "关注中...",
|
||
}
|
||
);
|
||
|
||
if (response.code === 0) {
|
||
return !is_following;
|
||
} else {
|
||
throw new Error(response.message || "操作失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("关注操作失败:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 保存用户信息
|
||
static async save_user_info(
|
||
user_info: Partial<UserInfo> & { phone?: string; gender?: string }
|
||
): Promise<boolean> {
|
||
try {
|
||
// 字段映射配置
|
||
const field_mapping: Record<string, string> = {
|
||
nickname: "nickname",
|
||
avatar: "avatar_url",
|
||
gender: "gender",
|
||
phone: "phone",
|
||
latitude: "latitude",
|
||
longitude: "longitude",
|
||
province: "province",
|
||
country: "country",
|
||
city: "city",
|
||
personal_profile: "personal_profile",
|
||
occupation: "occupation",
|
||
ntrp_level: "ntrp_level",
|
||
};
|
||
|
||
// 构建更新参数,只包含非空字段
|
||
const updateParams: Record<string, string> = {};
|
||
|
||
// 循环处理所有字段
|
||
Object.keys(field_mapping).forEach((key) => {
|
||
const value = user_info[key as keyof typeof user_info];
|
||
if (value && typeof value === "string" && value.trim() !== "") {
|
||
updateParams[field_mapping[key]] = value.trim();
|
||
}
|
||
});
|
||
|
||
// 如果没有需要更新的字段,直接返回成功
|
||
if (Object.keys(updateParams).length === 0) {
|
||
console.log("没有需要更新的字段");
|
||
return true;
|
||
}
|
||
|
||
const response = await httpService.post<any>(
|
||
API_CONFIG.USER.UPDATE,
|
||
updateParams,
|
||
{
|
||
showLoading: true,
|
||
loadingText: "保存中...",
|
||
}
|
||
);
|
||
|
||
if (response.code === 0) {
|
||
return true;
|
||
} else {
|
||
throw new Error(response.message || "更新用户信息失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("保存用户信息失败:", error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
// 获取用户动态
|
||
static async get_user_activities(
|
||
user_id: string,
|
||
page: number = 1,
|
||
limit: number = 10
|
||
): Promise<any[]> {
|
||
try {
|
||
const response = await httpService.post<any>(
|
||
"/user/activities",
|
||
{
|
||
user_id,
|
||
page,
|
||
limit,
|
||
},
|
||
{
|
||
showLoading: false,
|
||
}
|
||
);
|
||
|
||
if (response.code === 0) {
|
||
return response.data.activities || [];
|
||
} else {
|
||
throw new Error(response.message || "获取用户动态失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取用户动态失败:", error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 上传头像
|
||
static async upload_avatar(file_path: string): Promise<string> {
|
||
try {
|
||
// 先上传文件到服务器
|
||
const result = await uploadFiles.upload_oss_img(file_path);
|
||
|
||
await this.save_user_info({ avatar: result.ossPath });
|
||
|
||
// 使用新的响应格式中的file_url字段
|
||
return result.ossPath;
|
||
} catch (error) {
|
||
console.error("头像上传失败:", error);
|
||
// 如果上传失败,返回默认头像
|
||
return require("../static/userInfo/default_avatar.svg");
|
||
}
|
||
}
|
||
|
||
// 解析用户手机号
|
||
static async parse_phone(phone_code: string): Promise<string> {
|
||
try {
|
||
const response = await httpService.post<{ phone: string }>(
|
||
API_CONFIG.USER.PARSE_PHONE,
|
||
{ phone_code },
|
||
{
|
||
showLoading: true,
|
||
loadingText: "获取手机号中...",
|
||
}
|
||
);
|
||
if (response.code === 0) {
|
||
return response.data.phone || "";
|
||
} else {
|
||
throw new Error(response.message || "获取手机号失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取手机号失败:", error);
|
||
return "";
|
||
}
|
||
}
|
||
|
||
// 获取职业树
|
||
static async getProfessions(): Promise<[] | PickerOption[]> {
|
||
try {
|
||
const response = await httpService.post<any>(API_CONFIG.PROFESSIONS);
|
||
const { code, data, message } = response;
|
||
if (code === 0) {
|
||
return formatOptions(data || []);
|
||
} else {
|
||
throw new Error(message || "获取职业树失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取职业树失败:", error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 获取城市树
|
||
static async getCities(): Promise<[] | PickerOption[]> {
|
||
try {
|
||
const response = await httpService.post<any>(API_CONFIG.CITIS);
|
||
const { code, data, message } = response;
|
||
if (code === 0) {
|
||
return formatOptions(data || []);
|
||
} else {
|
||
throw new Error(message || "获取城市树失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("获取职业树失败:", error);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 注销账户
|
||
static async logout(): Promise<void> {
|
||
try {
|
||
const response = await httpService.post<any>(API_CONFIG.USER.LOGOUT);
|
||
const { code, message } = response;
|
||
if (code === 0) {
|
||
// 清除用户数据
|
||
clear_login_state();
|
||
|
||
Taro.reLaunch({
|
||
url: "/login_pages/index/index",
|
||
});
|
||
} else {
|
||
throw new Error(message || "注销账户失败");
|
||
}
|
||
} catch (error) {
|
||
console.error("注销账户失败:", error);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 从 loginService 移过来的用户相关方法
|
||
|
||
// 获取用户详细信息
|
||
export const fetchUserProfile = async (): Promise<
|
||
ApiResponse<UserInfoType>
|
||
> => {
|
||
try {
|
||
const response = await httpService.post("user/detail");
|
||
return response;
|
||
} catch (error) {
|
||
console.error("获取用户信息失败:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// 获取昵称修改状态
|
||
export const checkNicknameChangeStatus = async (): Promise<
|
||
ApiResponse<NicknameChangeStatus>
|
||
> => {
|
||
try {
|
||
const response = await httpService.post(
|
||
"/user/check_nickname_change_status"
|
||
);
|
||
return response;
|
||
} catch (error) {
|
||
console.error("获取昵称修改状态失败:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
// 修改昵称
|
||
export const updateNickname = async (nickname: string) => {
|
||
try {
|
||
const response = await httpService.post("/user/update_nickname", {
|
||
nickname,
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error("昵称修改失败:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// 更新用户信息
|
||
export const updateUserProfile = async (payload: Partial<UserInfoType>) => {
|
||
try {
|
||
const response = await httpService.post("/user/update", payload);
|
||
return response;
|
||
} catch (error) {
|
||
console.error("更新用户信息失败:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// 更新用户坐标位置
|
||
export const updateUserLocation = async (
|
||
latitude: number,
|
||
longitude: number
|
||
) => {
|
||
try {
|
||
const response = await httpService.post("/user/update_location", {
|
||
latitude,
|
||
longitude,
|
||
});
|
||
return response;
|
||
} catch (error) {
|
||
console.error("更新用户坐标位置失败:", error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// 获取用户信息(从本地存储)
|
||
export const get_user_info = (): any | null => {
|
||
try {
|
||
let userinfo = Taro.getStorageSync("user_info");
|
||
if (userinfo) {
|
||
return JSON.parse(userinfo);
|
||
}
|
||
return null;
|
||
} catch (error) {
|
||
return null;
|
||
}
|
||
};
|
||
|
||
// 客服中心处理函数
|
||
export const handleCustomerService = async (): Promise<void> => {
|
||
try {
|
||
// 获取当前环境的客服配置
|
||
const config = getCurrentConfig;
|
||
const { customerService } = config;
|
||
|
||
console.log("打开客服中心,配置信息:", customerService);
|
||
|
||
// 使用微信官方客服能力
|
||
await Taro.openCustomerServiceChat({
|
||
extInfo: {
|
||
url: customerService.serviceUrl,
|
||
},
|
||
corpId: customerService.corpId,
|
||
success: (res) => {
|
||
console.log("打开客服成功:", res);
|
||
},
|
||
fail: (error) => {
|
||
console.error("打开客服失败:", error);
|
||
// 如果官方客服不可用,显示备用联系方式
|
||
showCustomerServiceFallback(customerService);
|
||
},
|
||
});
|
||
} catch (error) {
|
||
console.error("客服功能异常:", error);
|
||
// 备用方案:显示联系信息
|
||
showCustomerServiceFallback();
|
||
}
|
||
};
|
||
|
||
// 客服备用方案
|
||
const showCustomerServiceFallback = (customerInfo?: any) => {
|
||
const options = ["拨打客服电话", "复制邮箱地址"];
|
||
|
||
// 如果没有客服信息,只显示通用提示
|
||
if (!customerInfo?.phoneNumber && !customerInfo?.email) {
|
||
Taro.showModal({
|
||
title: "联系客服",
|
||
content: "如需帮助,请通过其他方式联系我们",
|
||
showCancel: false,
|
||
});
|
||
return;
|
||
}
|
||
|
||
Taro.showActionSheet({
|
||
itemList: options,
|
||
success: async (res) => {
|
||
if (res.tapIndex === 0 && customerInfo?.phoneNumber) {
|
||
// 拨打客服电话
|
||
try {
|
||
await Taro.makePhoneCall({
|
||
phoneNumber: customerInfo.phoneNumber,
|
||
});
|
||
} catch (error) {
|
||
console.error("拨打电话失败:", error);
|
||
Taro.showToast({
|
||
title: "拨打电话失败",
|
||
icon: "none",
|
||
});
|
||
}
|
||
} else if (res.tapIndex === 1 && customerInfo?.email) {
|
||
// 复制邮箱地址
|
||
try {
|
||
await Taro.setClipboardData({
|
||
data: customerInfo.email,
|
||
});
|
||
Taro.showToast({
|
||
title: "邮箱地址已复制",
|
||
icon: "success",
|
||
});
|
||
} catch (error) {
|
||
console.error("复制邮箱失败:", error);
|
||
Taro.showToast({
|
||
title: "复制失败",
|
||
icon: "none",
|
||
});
|
||
}
|
||
}
|
||
},
|
||
});
|
||
};
|