Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Ajax回调不重建表单drupal 8

Ajax回调不重建表单drupal 8
EN

Stack Overflow用户
提问于 2021-02-03 16:45:29
回答 1查看 613关注 0票数 0

我已经创建了一个具有两个ajax回调的表单,但它们似乎都不能按预期工作。form_state在回调后不会重新构建

代码语言:javascript
运行
AI代码解释
复制
namespace Drupal\dashboard\Form;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\user\Entity\User;
use Drupal\commerce_price\Price;
use Drupal\commerce_order\Entity\OrderItem;
use Drupal\commerce_order\Entity\Order;
use Drupal\taxonomy\Entity\Term;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
use CommerceGuys\Addressing\AddressFormat\AddressField;
use Drupal\dashboard\DashboardRepository;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 *  UI to update a record.
 *
 * @ingroup scorecards
 */
class AcfDataUpdateForm extends FormBase {

  /**
   * Our database repository service.
   *
   * @var \Drupal\scorecards\ScorecardsRepository
   */
  protected $repository;

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'acf_data_update_form';
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    $form = new static($container->get('dashboard.repository'));
    $form->setStringTranslation($container->get('string_translation'));
    $form->setMessenger($container->get('messenger'));
    return $form;
  }

  /**
   * Construct the new form object.
   */
  public function __construct(DashboardRepository $repository) {
    $this->repository = $repository;
  }

  /**
   * Sample UI to update a record.
   */
public function buildForm(array $form, FormStateInterface $form_state, $order_id = NULL) {
$items = $this->repository->get_order_items($order_id);
$mark_pack = 6;
$uid = \Drupal::currentUser()->id();
$config = \Drupal::config('dashboard.settings');  
foreach($items as $item){
 if($item->purchased_entity == 6 || $item->purchased_entity == 7 || $item->purchased_entity == 8){
    $mark_pack = $item->purchased_entity; 
   }    
    
}
$form['#prefix'] = '<div id="package_design_wrapper-container">';
$form['#suffix'] = '</div>';
$form['order_id'] = array(
'#type' => 'hidden',
'#default_value' => $order_id,
);
$form['package'] = array(
 '#type' => 'container',
 '#prefix' => '<div class="message" id="message"></div>',
 '#attributes' => array('id' => 'marketing-package-wrapper'),
);  
$form['package']['marketing_package'] = array(
  '#type' => 'radios',
  '#default_value' => $mark_pack,
  '#options' => array(
    6 => $this
      ->t('Standard <span>'.$this->getPrice(6).'</span>'),
    7 => $this
      ->t('Premium <span>'.$this->getPrice(7).'</span>'),
    8 => $this
      ->t('Premium + <span>'.$this->getPrice(8).'</span>'),  
  ),  
  '#disabled' => true,
);

$form['package']['designation'] = array(
  '#type' => 'textfield',
  '#prefix' => $this->t('<h3>your designation</h3><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry</p>'),
  '#attributes' => array('placeholder' => 'Type Here'),
);

$form['package']['photograph'] = array(
  '#type' => 'managed_file',
  '#upload_location' => 'public://acf_profile_photos',
  '#multiple'        => FALSE,
  '#upload_validators'    => [
    'file_validate_is_image'      => [],
    'file_validate_extensions'    => array('png jpg jpeg'),
    'file_validate_size'          => array(25600000)
        ],
  '#required' => true,
  '#title' => $this->t('<h3>your photograph</h3><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry</p>'),
);

$form['package']['date'] = array(
  '#type' => 'radios',
  '#default_value' => 1,
  '#options' => $this->getDates(),
  '#required' => true,
  '#prefix' => $this->t('<h3>Award Slot</h3><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry</p>'),
  '#ajax' => [
       'wrapper' => 'wrapper-dropdown',
       'callback' => '::getAwardSlots',
       'method' => 'replace',
       'effect' => 'fade',
       'event' => 'change',
       'progress' => [
             'type' => 'throbber',
             'message' => NULL,
          ],
      ],
);

$form['package']['award_slot'] = array(
  '#type' => 'radios',
  '#default_value' => 1,
  '#options' => $form_state->getValue('date') ? $this->getAwardSlotOptions($form_state->getValue('date')) : $this->getAwardSlotOptions('2021-04-21'),
  '#prefix' => '<div id="wrapper-dropdown">',
  '#suffix' => '</div>',
  '#required' => true,
);

$form['package']['bio'] = array(
  '#type' => 'textarea',
  '#prefix' => $this->t('<h3>Individual Bio</h3><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry</p>'),
  '#attributes' => array('placeholder' => 'Type Here'),
);

$form['package']['plaque_type'] = array(
  '#type' => 'radios',
  '#default_value' => 'remote',
  '#options' => array(
    'remote' => $this
      ->t('remote'),
    'event' => $this
      ->t('event'),   
  ),
  '#required' => true,
  '#prefix' => $this->t('<h3>Plaque Type</h3>'),
);

$form['package']['shipping_address'] = array(
 '#type' => 'container',
 '#states' => array(
    'visible' => array(
      ':input[name="plaque_type"]' => array(
        'value' => 'remote',
      ),
    ),
  ),
);
$form['package']['shipping_address']['markup'] = array(
  '#markup' => ' <h3>shipping address</h3><p>Lorem Ipsum is simply dummy text of the printing and typesetting industry</p>',
);

$form['package']['shipping_address']['address'] = array(
    '#type' => 'address',
    '#default_value' => ['country_code' => 'US'],
    '#used_fields' => [
      AddressField::GIVEN_NAME,
      AddressField::FAMILY_NAME,
      AddressField::ORGANIZATION,
      AddressField::ADDRESS_LINE1,
      AddressField::ADDRESS_LINE2,
      AddressField::LOCALITY,
      AddressField::POSTAL_CODE,
    ],
    '#available_countries' => ['US'],
    '#attributes' => array('class' => array('edit-address')),
);

$form['package']['save'] = array(
    '#type' => 'submit',
    '#submit' => ['::submitPackageDetails'],
    '#name' => 'mark-save',
    '#value' => t('Save'),
    '#validate' => ['::validatePackageDetails'],
    '#prefix' => '<div class="row"><div class="col-md-6">',
    '#suffix' => '</div>',
    '#ajax' => [
       'wrapper' => 'package_design_wrapper-container',
       'progress' => [
             'type' => 'throbber',
             'message' => NULL,
          ],
      ],
    );
$form['package']['submit'] = array(
    '#type' => 'submit',
    '#submit' => ['::submitPackageDetails'],
    '#validate' => ['::validatePackageDetails'],
    '#name' => 'mark-submit',
    '#value' => t('Submit'),
    '#prefix' => '<div class="col-md-6">',
    '#suffix' => '</div></div>',
    '#ajax' => [
       'wrapper' => 'package_design_wrapper-container',
       'progress' => [
             'type' => 'throbber',
             'message' => NULL,
          ],
      ],
    );
    
    
$form['terms'] = array(
   '#type' => 'checkbox',
   '#title' => t('I have read the <a href="/terms-of-service">terms & conditions</a> and agree to comply with them. '),
   '#prefix' => '<div class="row mb-3"><div class="col-12 col-sm-12 col-md-12 col-lg-12">',
   '#suffix' => '</div></div>', 


);
     
$form['#theme'] = 'acf_data_update_form';
return $form;   

 }
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
     echo "<pre>";
     print_r($form_state->getValues()); 
     die;
      
    $old = $this->repository->save_old_data($old_data, $order_id, $user_id);
    $this->messenger()->addMessage($this->t('Saved old data @entry (@count row updated)', [
      '@count' => $old,
      '@entry' => print_r($old_data, TRUE),
    ]));
    if(!empty($old)){ 
    $count = $this->repository->update_nominee($entry, $order_id, $user_id);
    $this->messenger()->addMessage($this->t('Updated entry @entry (@count row updated)', [ 
      '@count' => $count,
      '@entry' => print_r($entry, TRUE),
    ]));
    }
    $form_state->setRedirect('scorecards.scorecard_entry_list');
  }

  public function validatePackageDetails(array &$form, FormStateInterface $form_state){     
   $fields = array('designation','date','award_slot','plaque_type','bio','photograph');   
   $add_fields = array('given_name','family_name','organization','address_line1','locality','postal_code');     
   $valid = true;   
   if($form_state->getValue('plaque_type') == 'remote'){
    $add = $form_state->getValue('address');       
    foreach($add_fields as $field){
     if(empty($add[$field])){
     $form_state->setErrorByName($add[$field], 'Please enter a valid detail');  
      }    
     }
    }             
   foreach($fields as $field){
     if(empty($form_state->getValue($field))){
      $form_state->setErrorByName($field, 'Please enter a valid detail');
        }      
     }        
  
   
   }
   
  public function submitPackageDetails(array &$form, FormStateInterface $form_state){
      $fields = array('designation','date','award_slot','plaque_type','bio','photograph');    
      $add_fields = array('given_name','family_name','organization','address_line1','locality','postal_code'); 
        if($form_state->getValue('plaque_type') == 'remote'){
         $add = $form_state->getValue('address');  
         }
        $data = array();
        $pid = $form_state->getValue('marketing_package');            
       foreach($fields as $field){
        $data[$field] = $form_state->getValue($field);     
         }
    $data['status'] = 0;     
    $triggering_element = $form_state->getTriggeringElement();
    $button_name = $triggering_element['#name'];
    if($button_name == 'mark-submit'){
     $data['status'] = 1;   
     }
    $order_id = $form_state->getValue('order_id');
    $pid = $form_state->getValue('marketing_package'); 
    $entry = $this->repository->saveAcfDetails($order_id,$pid,$data);
    $entry_add = $this->repository->saveShippingDetails($order_id,$add);
    $this->setFilePermanent($form_state->getValue('photograph')); 
    if($entry && $entry_add){
        $form_state->setRebuild(TRUE);
        $this->messenger->addMessage(t('Your data has been saved successfully!'));
        }
      else{
        $this->messenger->addMessage(t('Your data cannot be Saved'));  
         }            
    } 


  function getPrice($id){
   $variation = \Drupal\commerce_product\Entity\ProductVariation::load($id); 
   return str_replace('USD','$',$variation->getPrice());
   }
  
  function setFilePermanent($image_id){
     if(!empty($image_id)) {
      $file = \Drupal\file\Entity\File::load($image_id[0]);
     if (isset($file) and is_object($file)) {
        $file->setPermanent();  // FILE_STATUS_PERMANENT;
        $file->save();
        }  
      
      }
    }  
  function getDates(){
   $config = $this->config('dashboard.settings');     
   $start_date = $config->get('conference_start_date');
   $no_days = $config->get('no_days');
   $dates = array(); 
   for($i = 1; $i <= $no_days; $i++){
      $j = $i-1;  
     $date = strtotime("{$j} day", strtotime($start_date));
     $new_date = date("d-M-Y", $date);
     $dates[date("Y-m-d", $date)] =  'DAY '.$i.'<span>('.$new_date.')</span>'; 
      }
    return $dates;  
      
      }
  
  function getAwardSlots(array &$form, FormStateInterface $form_state){
    $form_state->setValue('date', $form_state->getValue('date'));
    $form_state->setRebuild(TRUE);
    return $form['package']['award_slot'];      
    }
    
  function getAwardSlotOptions($date){
    $terms = \Drupal::entityManager()->getStorage('taxonomy_term')->loadByProperties(array('name' => $date));
    $term = reset($terms); 
    $award_slots = $term->get('field_award_slot');
    $slots = array();
    foreach($award_slots as $award_slot){ 
     $slots[$award_slot->value] = $award_slot->value;
     }
     return $slots;
     }  
  
}

我还为这个表单创建了一个模板

代码语言:javascript
运行
AI代码解释
复制
                        <div class="row">
                            <div class="col-12 col-sm-12 col-md-9 col-lg-9">
                                <div class="package_border">
                                    <div class="wellcome_back">
                                        <div class="row">
                                            <div class="col-12 col-sm-12 col-md-12 col-lg-12">
                                                
                                                    <h3>Awardee Deliverables</h3>
                                
                                            </div>
                                        </div>
                                    </div>
                                    <div class="package_header">
                                    <div class="row">
                                        <div class="col-12 col-sm-12 col-md-5 col-lg-5">
                                            <div class="package_header_batch">
                                                <img src="/modules/custom/dashboard/images/package.jpg">
                                                <div>
                                                    <h2>Marketing package</h2>
                                                    <p>Lorem Ipsum is simply</p>
                                                </div>
                                            </div>
                                        </div>
                                        {% if form.package.marketing_package %}
                                        <div class="col-12 col-sm-12 col-md-7 col-lg-7">
                                         {{ form.package.marketing_package }}   
                                        </div>
                                        {% endif %}
                                    </div>
                                </div>
                                <div class="designation_content_wrapper">
                                    <div class="designation_content">
                                    {{ form.package|without('marketing_package','plaque_type','shipping_address','submit','save') }}
                                    {% if form.package.plaque_type %}
                                    <h3>plaque</h3>
                                    <p>Lorem Ipsum is simply dummy text of the printing and typesetting industry</p>
                                    <div class="row mb-5">
                                            <div class="col-12 col-sm-12 col-md-6 col-lg-6">
                                                <figure class="plaque_figure">
                                                    {% set images = getPlaqueBadge() %}
                                                    <img src="{{ images.plaque }}" width="100%">
                                                </figure>
                                            </div>
                                            <div class="col-12 col-sm-12 col-md-6 col-lg-6">
                                                <div class="plaque_type_wrapper">
                                                {{ form.package.plaque_type }}
                                                </div>
                                            </div>
                                        </div>
                                        {% endif %}
                                        {% if form.package.shipping_address %}
                                         {{ form.package.shipping_address }}
                                        {% endif %}
                                        {{ form.package.save }}
                                        {{ form.package.submit }}
                                        
                                    </div>
                                </div>
                                </div>
                                {{ form|without('package') }}
                                
                            </div> 
                                 <div class="col-12 col-sm-12 col-md-3 col-lg-3">
                                <aside class="package_aside">
                                    <div class="package_aside_wrapper">
                                        <h5>jump to</h5>
                                        <ul class="package_aside_list">
                                            <li>
                                                <a href="#" class="active">designation</a>
                                            </li>
                                            <li>
                                                <a href="#">photograph</a>
                                            </li>
                                            <li>
                                                <a href="#">award slot</a>
                                            </li>
                                            <li>
                                                <a href="#">individual boi</a>
                                            </li>
                                            <li>
                                                <a href="#">plaque</a>
                                            </li>
                                            <li>
                                                <a href="#">plaque type</a>
                                            </li>
                                            <li>
                                                <a href="#">shipping address</a>
                                            </li>
                                        </ul>
                                    </div>
                                </aside>
                            </div>  
                            
                        </div>

实际发生的情况是,在执行ajax回调之后,form_state仍然有用户提交的值,而这是不应该的。如果有人知道我做错了什么,请帮助我。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-07-21 10:00:07

这有点老了,但也许可以帮助某人:)。

首先,您不能从ajax回调中修改$form_state,因此$form_state->setRebuid()将不起作用。

AJAX form docs中所述,您应该只返回标记、可呈现数组或AjaxCommand。

这里需要做的是修改ajax回调中元素的值,更像这样:

代码语言:javascript
运行
AI代码解释
复制
function getAwardSlots(array &$form, FormStateInterface $form_state){
  $date = $form_state->getValue('date')
  $form['package']['award_slot']['#options'] = $this->getAwardSlotOptions($date);

  return $form['package']['award_slot'];      
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/66031948

复制
相关文章
SwiftUI:猜国旗项目 堆叠按钮
我们将通过构建基本的UI结构来启动我们的应用程序,这将会是两个标签告诉用户该做什么,然后是三个显示三个世界国家的国旗按钮。
韦弦zhy
2020/03/27
1K0
MongoDB更改oplog大小
在生产环境新增secondary:10.9.197.6:27017 ,数据量140G,却同步了一天还未追上数据,通过如下方式查看同步情况:
星哥玩云
2022/08/17
1.7K0
MongoDB更改oplog大小
bootstrap 按钮大小
<div clas="form-group"> <button class="btn btn-lg">btn-lg</button> <button class="btn">btn</button> <button class="btn btn-sm">btn-sm</button> <button class="btn btn-xs">btn-xs</button> <button class="btn btn-block">btn-block</button> </div>
用户5760343
2022/01/10
8260
bootstrap 按钮大小
mac更改launchpad图标大小
launchpad #0 GitHub None #2 环境 Mac #3 需求分析 launchpad的图标太大,需要更改成小的 #4 开始 打开终端 输入命令 defaults write com.apple.dock springboard-columns int 8 # 一行8个 重启launchpad killall Dock
Autooooooo
2020/11/09
1.5K0
HPUX调整LUN大小识别更改
磁盘阵列通常允许调整 LUN 的大小,如果增加 LUN 的大小,请执行以下步骤将附加空间合并到卷组中: 1、按照阵列说明增加 LUN 的大小。 2、运行 vgmodify 检测任何物理卷大小更改。还将报告卷组能否使用所有空间。 3、如果 vgmodify 报告,每个物理卷的最大物理盘区数 (max_pe) 太小,无法容纳新增的空     间,请使用带 -t 和 -n 选项的 vgmodify 确定 max_pe 的新值,如“修改卷组参数” 4、按照新的设置运行带 -r 选项的 vgmodify 检查这些值。 5、停用卷组。 6、提交 max_pe 的任何新值,运行不带 -r 选项的 vgmodify 更新物理卷信息。 7、激活卷组。运行 vgdisplay 和 pvdisplay 命令验证增加的空间是否可用。
星哥玩云
2022/06/30
1.6K0
C#实现利用单选框实现更改文本的richTextBox字体、大小、加粗
C#实现利用单选框实现更改文本的richTextBox字体、大小、加粗。通过选择字体、大小和是否加粗决定,我们在文本框中字体格式是什么。
跋扈洋
2021/04/25
4.7K0
C#实现利用单选框实现更改文本的richTextBox字体、大小、加粗
CSS样式更改——文本Content
上篇文章主要讲述了CSS样式更改中的背景Background,这篇文章我们来谈谈文本Content内容的基础用法。
前端皮皮
2020/11/26
1.7K0
iOS 点击按钮复制文本
UIPasteboard *pab = [UIPasteboard generalPasteboard]; NSString *string = @"测试"; pab.string = string; if (pab == nil) { [MBProgressHUD showError:@"复制失败"]; }else {
Lee坚武
2020/10/10
4.3K0
iOS 点击按钮复制文本
SwiftUI案例:尺寸自适应文本框
SwiftUI 并未提供可自适应高度的文本框组件,为实现自适应高度则需要继承 UITextField 进而自定义封装一个弹性的文本框组件。 通过更新函数,从该弹性文本框中获得文本内容的高度并将其赋值给组件的高度,即可实现“弹性”伸缩的效果。
DioxideCN
2022/08/05
3.4K0
SwiftUI案例:尺寸自适应文本框
更改iis上传文件的默认大小
第一步:修改IIS设置,允许直接编辑配置数据库。先打开,Internet信息服务 第二步:先在服务里关闭iis admin service服务,找到windows\system32\inetsrv\下的metabase.xml, 打开,找到ASPMaxRequestEntityAllowed 把他修改为需要的值,默认为204800,即200K,如把它修改为102400000(100M)。 然后重启iis admin service服务。 办法二: 新建一个文本文件,内容如下: set providerObj=GetObject(“winmgmts:/root/MicrosoftIISv2”) set vdirObj=providerObj.get(“IIsWebVirtualDirSetting=’W3SVC/1/ROOT'”) WScript.Echo “Before: ” & vdirObj.AspMaxRequestEntityAllowed vdirObj.AspMaxRequestEntityAllowed=102400000 vdirObj.Put_() WScript.Echo “Now: ” & vdirObj.AspMaxRequestEntityAllowed
全栈程序员站长
2022/07/05
2.6K0
更改文字、图片和视频大小(缩放)
您可以更改所访问网页中所有内容(包括文字、图片和视频)的大小,也可以仅更改字体大小。
全栈程序员站长
2022/07/11
2.2K0
更改文字、图片和视频大小(缩放)
更改Android Studio 的log的大小
在File -> Setting 中, 有Override console cycle buffer size可以修改log大小. 建议修改后面加两个0, 100M.
望天
2022/01/08
1.8K0
更改Android Studio 的log的大小
QPushButton适应文本大小长度
使用QFontMetrics类的boundingRect成员函数计算给定文字得到文本整体长度大小。 小例子 #include <QPushButton> #include <QApplication> static QSize getTextSize(const QString &text) { /* 设置字体属性 */ QFont font; font.setPixelSize(16); /* 设置字体信息 */ QFontMetrics metrics(fon
Qt君
2020/06/17
4.5K0
WPF 点击按钮时更改按钮样式界面效果的 XAML 实现方法
在 WPF 中按钮 Button 将会吃掉路由事件,此时的 EventTrigger 如果通过 RoutedEvent 是 MouseLeftButtonDown 那么将会拿不到路由事件,也就触发不了,因此样式将不会变更。简单的解决方法就是通过 VisualStateManager 配合 VisualState 来实现
林德熙
2020/08/31
4.5K0
为什么 SwiftUI 的修饰符顺序很重要
每当我们将修饰符应用于 SwiftUI 视图时,我们实际上都会创建一个,应用了更改的新视图 —— 我们不仅仅是修改现有的视图。 如果你仔细想想,这种行为是有道理的 —— 我们的视图仅保留我们赋予它们的确切属性,因此,如果我们设置背景颜色或字体大小,则无处存储该数据。
Swift社区
2021/11/26
2.4K0
为什么SwiftUI修饰符顺序很重要?
每当我们将修饰符应用于SwiftUI视图时,我们实际上都会创建一个应用了更改的新视图——我们不仅会修改现有的视图。如果您考虑一下,这种行为是有道理的——我们的视图仅保留我们赋予它们的确切属性,因此,如果我们设置背景颜色或字体大小,则无处存储该数据。
韦弦zhy
2020/03/26
2.4K0
关于bootstrap--表单(按钮<button>效果、大小、禁用) 以及 自定义按钮
注:虽然在Bootstrap框架中使用任何标签元素都可以实现按钮风格,但个人并不建议这样使用,为了避免浏览器兼容性问题,个人强烈建议使用button或a标签来制作按钮。
浩Coding
2019/07/03
2.5K0
在 SwiftUI 中用 Text 实现图文混排
SwiftUI 提供了强大的布局能力,不过这些布局操作都是在视图之间进行的。当我们想在 Text 中进行图文混排时,需要采用与视图布局不同的思路与操作方式。本文将首先介绍一些与 Text 有关的知识,并通过一个实际案例,为大家梳理出在 SwiftUI 中用 Text 实现图文混排的思路。
东坡肘子
2022/12/16
4.6K0
在 SwiftUI 中用 Text 实现图文混排
【说站】PDF如何更改页面尺寸大小,QI插件改变PDF页面大小
2、Quite Imposing plus3 PDF拼版插件中文汉化破解版(Acrobat Pro DC的QI插件)
很酷的站长
2022/11/24
2.9K0
【说站】PDF如何更改页面尺寸大小,QI插件改变PDF页面大小
点击加载更多

相似问题

在出现时禁用SwiftUI帧动画

224

SwiftUI更改macOS的文本大小

32

文本大小更改按钮

41

SwiftUI:调整文本大小以适应按钮

12

我想更改位置按钮swiftUI的大小

211
添加站长 进交流群

领取专属 10元无门槛券

AI混元助手 在线答疑

扫码加入开发者社群
关注 腾讯云开发者公众号

洞察 腾讯核心技术

剖析业界实践案例

扫码关注腾讯云开发者公众号
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档