123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823 |
- // pages/ai-plan/ai-plan.js
- const app = getApp();
- Page({
- data: {
- currentStep: 1,
- planGenerated: false,
- selectedDays: 3,
- selectedCities: [],
- selectedInterests: ['patriotism'], // 默认选中爱国主义
- isRegenerating: false, // 是否正在重新生成
- planData: null,
- selectedTransport: 'public',
- customRequirements: '',
- dayOptions: [
- { label: '1天', value: 1 },
- { label: '2天', value: 2 },
- { label: '3天', value: 3 },
- { label: '4天', value: 4 },
- { label: '5天', value: 5 },
- { label: '6天', value: 6 },
- { label: '7天', value: 7 }
- ],
- cityOptions: [],
- interestOptions: [
- { label: '革命历史', value: 'history' },
- { label: '红色教育', value: 'education' },
- { label: '党史学习', value: 'party-history' },
- { label: '爱国主义', value: 'patriotism' }
- ],
- transportOptions: [
- { label: '公共交通', value: 'public', icon: '/images/icons/bus.png' },
- { label: '自驾', value: 'drive', icon: '/images/icons/car.png' },
- { label: '步行', value: 'walk', icon: '/images/icons/walk.png' },
- { label: '骑行', value: 'bike', icon: '/images/icons/bike.png' }
- ],
- planData: null,
- totalAttractions: 0,
- currentDate: '',
- isLoading: false,
- apiStatus: {
- citiesLoaded: false,
- attractionsLoaded: false
- },
- // 新增景点详情相关数据
- showAttractionDetail: false,
- currentAttraction: null,
- attractionDetail: null
- },
- // 添加 deepMerge 方法
- deepMerge(target, source) {
- // 如果 source 不是对象,直接返回 target
- if (typeof source !== 'object' || source === null) {
- return target;
- }
- // 遍历 source 的所有属性
- for (const key in source) {
- if (source.hasOwnProperty(key)) {
- // 如果属性值是对象且不是数组,递归合并
- if (typeof source[key] === 'object' && source[key] !== null && !Array.isArray(source[key])) {
- if (!target[key]) {
- target[key] = {};
- }
- this.deepMerge(target[key], source[key]);
- } else {
- // 否则直接赋值
- target[key] = source[key];
- }
- }
- }
- return target;
- },
- onLoad() {
- this.initPage();
- this.loadInitialData();
- },
- initPage() {
- const date = new Date();
- this.setData({
- currentDate: `${date.getMonth() + 1}月${date.getDate()}日`
- });
- this.initDefaultImages();
- },
- initDefaultImages() {
- this.setData({
- transportOptions: this.data.transportOptions.map(item => ({
- ...item,
- icon: this.checkImageExists(item.icon) ? item.icon : '/images/default-transport.png'
- }))
- });
- },
- loadInitialData() {
- this.getCities();
- },
- getCities() {
- this.setData({
- isLoading: true,
- cityOptions: []
- });
-
- wx.request({
- url: app.globalData.apiBaseUrl + '/api/cities/',
- method: 'GET',
- success: (res) => {
- if (res.statusCode === 200) {
- const rawData = res.data.data || res.data;
- const cityOptions = rawData.map(city => ({
- label: city.name || '未知城市',
- value: city.id ? city.id.toString() : '0',
- disabled: city.is_available === false
- }));
-
- this.setData({
- cityOptions,
- 'apiStatus.citiesLoaded': true
- });
- } else {
- this.handleApiError(res);
- }
- },
- fail: this.handleNetworkError,
- complete: () => {
- this.setData({ isLoading: false });
- }
- });
- },
- checkImageExists(path) {
- try {
- const fs = wx.getFileSystemManager();
- return fs.accessSync(path) === undefined;
- } catch (e) {
- return false;
- }
- },
- handleImageError(e) {
- const { type, index } = e.currentTarget.dataset;
- const defaultImages = {
- transport: '/images/default-transport.png',
- attraction: '/images/default-attraction.jpg',
- loading: '/images/loading.gif',
- success: '/images/success.png',
- arrow: '/images/icons/arrow.png',
- checked: '/images/icons/checked.png'
- };
-
- if (type === 'transport') {
- const key = `transportOptions[${index}].icon`;
- this.setData({ [key]: defaultImages.transport });
- } else if (type === 'attraction') {
- const path = `planData.days[${e.currentTarget.dataset.day}].attractions[${index}].image`;
- this.setData({ [path]: defaultImages.attraction });
- } else if (defaultImages[type]) {
- this.setData({ [e.currentTarget.dataset.src || 'imageUrl']: defaultImages[type] });
- }
- },
- selectDays(e) {
- this.setData({ selectedDays: e.currentTarget.dataset.value });
- },
- toggleCity(e) {
- const value = e.currentTarget.dataset.value;
- const index = this.data.selectedCities.indexOf(value);
- let newCities = [...this.data.selectedCities];
-
- if (index === -1) {
- newCities.push(value);
- } else {
- newCities.splice(index, 1);
- }
-
- this.setData({ selectedCities: newCities });
- },
- toggleInterest(e) {
- const value = e.currentTarget.dataset.value;
- const index = this.data.selectedInterests.indexOf(value);
- let newInterests = [...this.data.selectedInterests];
-
- if (index === -1) {
- newInterests.push(value);
- } else {
- newInterests.splice(index, 1);
- }
-
- this.setData({ selectedInterests: newInterests });
- },
- selectTransport(e) {
- this.setData({ selectedTransport: e.currentTarget.dataset.value });
- },
- updateCustomRequirements(e) {
- this.setData({ customRequirements: e.detail.value });
- },
- validateInputs() {
- if (this.data.selectedCities.length === 0) {
- wx.showToast({ title: '请至少选择一个城市', icon: 'none' });
- return false;
- }
- if (this.data.selectedDays < 1) {
- wx.showToast({ title: '请选择有效天数', icon: 'none' });
- return false;
- }
- return true;
- },
- goToNextStep() {
- if (this.data.currentStep === 1) {
- if (!this.validateInputs()) return;
-
- this.setData({ currentStep: 2 });
- this.generatePlanWithAI();
- } else if (this.data.currentStep === 2) {
- this.setData({ currentStep: 3 });
- }
- },
- // 生成行程
- generatePlanWithAI() {
- if (this.data.isLoading) return;
-
- // 验证输入
- if (this.data.selectedCities.length === 0) {
- wx.showToast({ title: '请至少选择一个城市', icon: 'none' });
- return;
- }
- // 显示加载状态
- this.setData({
- isLoading: true,
- planGenerated: false,
- planData: null
- });
- wx.showLoading({
- title: 'AI正在规划红色路线...',
- mask: true
- });
- // 准备请求数据
- const requestData = {
- city_ids: this.data.selectedCities,
- days: this.data.selectedDays,
- interests: [...this.data.selectedInterests, 'red-tourism'],
- transport: this.data.selectedTransport,
- custom_requirements: this.data.customRequirements
- };
- // 调用API
- wx.request({
- url: app.globalData.apiBaseUrl + '/api/red-tourism/plan/',
- method: 'POST',
- data: requestData,
- header: {
- 'Content-Type': 'application/json',
- 'Authorization': wx.getStorageSync('token') ? `Bearer ${wx.getStorageSync('token')}` : ''
- },
- success: (res) => {
- console.log('API响应:', res);
-
- if (res.statusCode === 200) {
- console.log('API返回数据:', JSON.stringify(res.data, null, 2)); // 添加这行
- if (res.data && res.data.status === 'success' && res.data.data) {
- // 处理并显示行程数据
- this.processAndShowPlan(res.data.data);
- } else {
- this.handleError('返回数据格式不正确');
- }
- } else {
- this.handleError(res.data?.message || `请求失败: ${res.statusCode}`);
- }
- },
- fail: (err) => {
- this.handleError('网络请求失败,请检查连接');
- },
- complete: () => {
- wx.hideLoading();
- this.setData({ isLoading: false });
- }
- });
- },
- processAndShowPlan(planData, isRegenerate = false) {
- // 默认值配置
- const DEFAULT_PLAN = {
- title: '红色文化之旅',
- description: '通过参观革命历史遗址学习党史',
- statistics: {
- red_attractions_count: 0,
- total_attractions: 0,
- educational_points_count: 0
- },
- red_tourism_tips: [
- "请着装整洁,保持肃穆",
- "建议提前学习相关历史知识"
- ],
- suitable_for: "适合党员干部、学生团体等",
- days: []
- };
- // 深度合并默认值
- const processedData = this.deepMerge(DEFAULT_PLAN, planData);
- // 处理每日行程
- processedData.days = (processedData.days || []).map((day, index) => {
- // 确保景点数据标准化
- const attractions = (day.attractions || []).map(att => ({
- id: att.id || 0,
- name: att.name || '红色教育基地',
- description: att.description || '',
- image: att.image || '/images/default-red.jpg',
- is_red_tourism: att.is_red_tourism !== false,
- educational_value: att.educational_value || '高',
- visit_time: att.visit_time || '09:00-17:00',
- ticket_price: att.ticket_price || '免费',
- address: att.address || '',
- open_hours: att.open_hours || '09:00-17:00',
- recommendation: att.recommendation || '建议参观时长1-2小时',
- history: att.history || '这里是重要的革命历史遗址',
- tags: att.tags || ['红色旅游']
- }));
- // 处理教育要点
- const educational_points = (day.educational_points || []).map(point => {
- return typeof point === 'string'
- ? { name: '教育要点', description: point, is_red_tourism: true }
- : {
- name: point.name || '教育要点',
- description: point.description || '',
- is_red_tourism: point.is_red_tourism !== false
- };
- });
- // 计算当日统计
- const dayStats = {
- red_attractions_count: attractions.filter(a => a.is_red_tourism).length,
- total_attractions: attractions.length,
- educational_points_count: educational_points.length
- };
- return {
- day: day.day || index + 1,
- theme: day.theme || '红色教育',
- transport: day.transport || '公共交通',
- meals: day.meals || {
- breakfast: '酒店早餐',
- lunch: '景区附近餐厅',
- dinner: '当地特色餐馆'
- },
- attractions,
- educational_points,
- summary: day.summary || '',
- accommodation: day.accommodation || '当地酒店',
- statistics: dayStats
- };
- });
- // 计算总统计
- processedData.statistics = processedData.days.reduce((stats, day) => {
- stats.red_attractions_count += day.statistics.red_attractions_count;
- stats.total_attractions += day.statistics.total_attractions;
- stats.educational_points_count += day.statistics.educational_points_count;
- return stats;
- }, {
- red_attractions_count: 0,
- total_attractions: 0,
- educational_points_count: 0
- });
- // 更新UI
- this.setData({
- planData: processedData,
- planGenerated: true,
- isLoading: false,
- lastGeneratedAt: processedData.generated_at || new Date().toISOString(),
- isRegenerated: isRegenerate
- });
- },
- // 新增方法处理景点数据
- processAttractions(attractions) {
- if (!Array.isArray(attractions)) return [];
-
- return attractions.map(att => ({
- id: att.id || 0,
- name: att.name || '红色教育基地',
- description: att.description || '',
- image: att.image || '/images/default-red.jpg',
- is_red_tourism: att.is_red_tourism !== false,
- educational_value: att.is_red_tourism ? (att.educational_value || '高') : '普通',
- visit_time: att.visit_time || '09:00-17:00',
- ticket_price: att.ticket_price || '免费',
- tags: Array.isArray(att.tags) ? att.tags : ['红色旅游']
- }));
- },
- // 错误处理
- handleError(message) {
- console.error('Error:', message);
- wx.showToast({
- title: message,
- icon: 'none',
- duration: 2000
- });
- this.setData({
- currentStep: 1,
- isLoading: false,
- planGenerated: false
- });
- },
- handleApiError(res) {
- let errorMsg = '生成失败';
- if (res.data && res.data.message) {
- errorMsg = res.data.message;
- } else if (res.statusCode === 500) {
- errorMsg = '服务器错误';
- }
- wx.showToast({ title: errorMsg, icon: 'none' });
- this.setData({ currentStep: 1 }); // 退回第一步
- },
- handleNetworkError() {
- wx.showToast({ title: '网络连接失败', icon: 'none' });
- this.setData({ currentStep: 1 });
- },
-
- // 计算红色景点数量
- calculateRedAttractions(planData) {
- return planData.days.reduce((total, day) => {
- return total + (day.attractions || []).filter(a =>
- a.is_red_tourism ||
- (a.tags && a.tags.some(tag => ['红色旅游', '革命', '烈士'].includes(tag)))
- ).length;
- }, 0);
- },
-
- // 提取教育要点
- extractEducationalPoints(planData) {
- const points = new Set();
-
- (planData.days || []).forEach(day => {
- // 从每日主题中提取
- if (day.theme && day.theme.includes('革命')) {
- points.add('学习' + day.theme);
- }
-
- // 从景点中提取
- (day.attractions || []).forEach(attraction => {
- if (attraction.educational_value) {
- points.add(attraction.educational_value);
- }
- });
- });
-
- // 默认教育要点
- if (points.size === 0) {
- return [
- "了解革命历史背景",
- "学习先烈精神",
- "培养爱国情怀"
- ];
- }
-
- return Array.from(points);
- },
-
- // 格式化红色旅游数据
- formatRedTourismPlanData(apiData) {
- const data = apiData.data || apiData;
- const defaultRedImage = '/images/red-attraction-default.jpg';
-
- return {
- title: data.title || `${this.data.selectedDays}天红色之旅`,
- description: data.description || "重温革命历史,传承红色精神",
- days: (data.days || []).map(day => ({
- day: day.day || 1,
- theme: day.theme || `红色教育第${day.day}天`,
- description: day.description || "",
- transport: day.transport || this.data.selectedTransport,
- educational_points: day.educational_points || [
- "学习革命精神",
- "传承红色基因",
- "培养爱国情怀"
- ],
- attractions: (day.attractions || []).map(att => ({
- id: att.id || 0,
- name: att.name || "红色教育基地",
- shortDesc: att.short_desc || att.description || "革命历史教育基地",
- description: att.description || "",
- image: att.image || defaultRedImage,
- tags: att.tags || ['红色旅游'],
- is_red_tourism: att.tags ?
- att.tags.some(tag => ['红色旅游', '革命', '烈士'].includes(tag)) : true,
- educational_value: att.educational_value || "高",
- visit_time: att.visit_time || "09:00-17:00",
- ticket_price: att.ticket_price || "免费",
- open_hours: att.open_hours || "09:00-17:00",
- address: att.address || "",
- recommendation: att.recommendation || "建议参观时长1-2小时",
- history: att.history || "这里是重要的革命历史遗址"
- }))
- })),
- red_tourism_tips: data.red_tourism_tips || [
- "请着装整洁,保持肃穆",
- "可以准备鲜花进行瞻仰",
- "建议提前学习相关历史知识"
- ],
- suitable_for: data.suitable_for || "适合所有人群"
- };
- },
-
- // 错误处理
- handleApiError(res) {
- let errorMsg = '生成红色路线失败';
-
- if (res.data && res.data.message) {
- errorMsg = res.data.message;
- } else if (res.statusCode === 400) {
- errorMsg = '请求参数有误,请检查城市选择';
- } else if (res.statusCode === 500) {
- errorMsg = '服务器处理失败,请稍后再试';
- }
-
- wx.showToast({
- title: errorMsg,
- icon: 'none',
- duration: 2000
- });
-
- this.setData({
- isLoading: false,
- generatingProgress: 0,
- currentStep: 1
- });
- wx.hideLoading();
- },
-
- handleNetworkError(err) {
- console.error('API请求失败:', err);
- wx.showToast({
- title: '网络连接失败,请重试',
- icon: 'none',
- duration: 2000
- });
-
- this.setData({
- isLoading: false,
- generatingProgress: 0,
- currentStep: 1
- });
- wx.hideLoading();
- },
- // 查看景点详情
- viewAttractionDetail(e) {
- const attractionId = e.currentTarget.dataset.id;
- const attraction = this.findAttractionById(attractionId);
-
- if (attraction) {
- this.setData({
- showAttractionDetail: true,
- currentAttraction: attraction,
- attractionDetail: this.getAttractionDetailData(attraction)
- });
- }
- },
- // 获取景点详情数据
- getAttractionDetailData(attraction) {
- return {
- name: attraction.name,
- image: attraction.image,
- description: attraction.description,
- history: attraction.history,
- address: attraction.address,
- open_hours: attraction.open_hours,
- ticket_price: attraction.ticket_price,
- recommendation: attraction.recommendation,
- educational_value: attraction.educational_value,
- visiting_etiquette: [
- "请保持庄严肃穆",
- "勿大声喧哗",
- attraction.tags.includes('memorial') ? "纪念馆内请勿拍照" : ""
- ].filter(item => item),
- contact: attraction.contact || "暂无联系方式"
- };
- },
- // 关闭景点详情
- closeAttractionDetail() {
- this.setData({
- showAttractionDetail: false,
- currentAttraction: null,
- attractionDetail: null
- });
- },
- // 保存行程
- savePlan() {
- if (!this.data.planData || this.data.isLoading) return;
-
- this.setData({ isLoading: true });
- wx.showLoading({ title: '保存中...', mask: true });
-
- const saveData = {
- title: this.data.planData.title,
- description: this.data.planData.description,
- days: this.data.planData.days.length,
- is_red_tourism: true,
- day_plans: this.data.planData.days.map(day => ({
- day: day.day,
- theme: day.theme,
- description: day.description,
- transport: day.transport,
- attractions: day.attractions.map(attr => ({
- attraction_id: attr.id,
- order: attr.order || 1,
- visit_time: attr.visit_time,
- notes: attr.notes
- }))
- }))
- };
-
- wx.request({
- url: app.globalData.apiBaseUrl + '/api/plans/',
- method: 'POST',
- data: saveData,
- header: {
- 'Content-Type': 'application/json'
- },
- success: (res) => {
- if ([200, 201].includes(res.statusCode)) {
- this.setData({ currentStep: 3 });
- wx.showToast({
- title: '保存成功',
- icon: 'success',
- duration: 2000
- });
- } else {
- this.handleApiError(res);
- }
- },
- fail: this.handleNetworkError,
- complete: () => {
- wx.hideLoading();
- this.setData({ isLoading: false });
- }
- });
- },
- // 重新生成方法
- regeneratePlan() {
- if (this.data.isRegenerating) return;
- this.setData({ isRegenerating: true });
- wx.showLoading({ title: '正在重新规划...', mask: true });
-
- const requestData = {
- // city_ids: this.data.selectedCities,
- // days: this.data.selectedDays,
- city_ids: this.data.selectedCities.map(Number), // 确保是数字数组
- days: Number(this.data.selectedDays),
- interests: this.data.selectedInterests,
- transport: this.data.selectedTransport,
- custom_requirements: this.data.customRequirements,
- previous_plan: this.data.planData || { days: [] }, // 必须包含原行程
- // previous_plan: this.data.planData
- };
-
- wx.request({
- url: app.globalData.apiBaseUrl + '/api/red-tourism/regenerate/',
- method: 'POST',
- data: JSON.stringify(requestData),
- header: {
- 'Content-Type': 'application/json',
- 'Authorization': wx.getStorageSync('token') ? `Bearer ${wx.getStorageSync('token')}` : ''
- },
- success: (res) => {
- console.log('API返回结果:', JSON.stringify(res.data, null, 2)); // 新增的打印语句
- if (res.statusCode === 200 && res.data.status === 'success') {
- // 处理返回的完整景点信息
- const planData = this._processPlanData(res.data.data);
- this.setData({
- planData,
- showDetail: true // 显示详情视图
- });
- wx.showToast({ title: '重新生成成功', icon: 'success' });
- } else {
- wx.showToast({
- title: res.data.message || '重新生成失败',
- icon: 'none',
- duration: 2000
- });
- }
- },
- fail: (err) => {
- console.error('API请求失败:', err);
- wx.showToast({ title: '网络请求失败', icon: 'none' });
- },
- complete: () => {
- this.setData({ isRegenerating: false });
- wx.hideLoading();
- }
- });
- },
-
- _processPlanData(planData) {
- // 确保每个景点都有完整信息
- planData.days.forEach(day => {
- day.attractions.forEach(att => {
- att.showDetail = false; // 控制详情展开状态
- if (!att.image) att.image = '/images/default-attraction.jpg';
- if (!att.description) att.description = '这里是红色教育基地,具有重要的历史教育意义';
- });
- });
- return planData;
- },
- findAttractionById(id) {
- for (const day of this.data.planData.days) {
- for (const attr of day.attractions) {
- if (attr.id == id) return attr;
- }
- }
- return null;
- },
- viewPlan() {
- wx.navigateTo({
- url: '/pages/plan-detail/plan-detail?plan=' +
- encodeURIComponent(JSON.stringify(this.data.planData)) +
- '&is_red_tourism=true'
- });
- },
- sharePlan() {
- if (!this.data.planData) return;
- wx.showShareMenu({ withShareTicket: true });
- },
- onShareAppMessage() {
- if (!this.data.planData) return {};
-
- return {
- title: this.data.planData.title || '我的红色之旅行程',
- path: '/pages/plan-detail/plan-detail?plan=' +
- encodeURIComponent(JSON.stringify(this.data.planData)) +
- '&is_red_tourism=true',
- imageUrl: this.data.planData.days[0]?.attractions[0]?.image ||
- '/images/red-tourism-share.jpg'
- };
- },
- handleApiError(res) {
- let errorMsg = '请求失败,请稍后再试';
-
- if (res.data?.message) {
- errorMsg = res.data.message;
- } else if (res.statusCode === 400) {
- errorMsg = '输入数据有误,请检查后重试';
- } else if (res.statusCode === 500) {
- errorMsg = '服务器错误,请稍后再试';
- }
-
- wx.showToast({ title: errorMsg, icon: 'none' });
- this.setData({ isLoading: false });
- },
- handleNetworkError(err) {
- let errorMsg = '网络连接失败';
-
- if (err.errMsg.includes('timeout')) {
- errorMsg = '请求超时,请检查网络连接';
- }
-
- wx.showToast({ title: errorMsg, icon: 'none' });
- this.setData({ isLoading: false });
- },
- navigateBack() {
- if (this.data.currentStep > 1) {
- this.setData({ currentStep: this.data.currentStep - 1 });
- } else {
- wx.navigateBack();
- }
- },
- onShow(){
- this.recordFeatureUsage('ai-plan');
- },
- recordFeatureUsage(featureName) {
- const app = getApp();
- console.log('发送的Token:', app.globalData.userInfo.token);
- console.log('发送的featureName:', featureName);
-
- wx.request({
- url: 'http://127.0.0.1:8000/api/record-usage/',
- method: 'POST',
- header: {
- 'Authorization': `Token ${app.globalData.userInfo.token}`,
- 'Content-Type': 'application/json' // 确保这个请求头存在
- },
- data: JSON.stringify({ // 关键修改:必须使用JSON.stringify
- feature_name: featureName // 参数名必须与后端一致
- }),
- success: (res) => {
- console.log('行为记录响应:', res.data);
- },
- fail: (err) => {
- console.error('请求失败:', err);
- }
- });
- },
- });
|