ExtJs学习(七)~~实战之订单模块 MVVM架构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaozhegaa/article/details/82874695

目录

 

目录

一、开发一个“商家订单”的功能~~ 效果图

二、开发架构 -- MVVM

三、开发思路 - - MVVM

四、开发代码 ~~ 以下采用MVVM分层的开发模式。

五、代码分享


 

 

一、开发一个“商家订单”的功能~~ 效果图

扫描二维码关注公众号,回复: 3433624 查看本文章

二、开发架构 -- MVVM

【what】

Model–View–ViewModel(MVVM) 是一个软件架构设计模式,由微软 WPF 和 Silverlight 的架构师 Ken Cooper 和 Ted Peters 开发,是一种简化用户界面的事件驱动编程方式。由 John Gossman(同样也是 WPF 和 Silverlight 的架构师)于2005年在他的博客上发表。

三、开发思路 - - MVVM

上一篇给大家演示了 反向思路,这次的话给大家演示正向思路,

正向思路:也就是从0开始开发,当我需要什么组件的时候就去开发使用什么组件。

first:订单模块主视图,因此需要一个主视图组件。— — Order.js

second:主视图中,放一个表格,因此需要一个表格组件。表格的column跟bbar跟Grid写在一个文件中 — — OrderGrid.js

third:GrId需要显示数据,因此需要一个数据源Store --  OrderStore.js 

four:而我们的Store数据源需要一个Model — — OrderModel.js

five:这一步很重要,就是将我们的数据绑定到ViewModel上,这一步是我们之前MVC架构没有的操作,参考下面的代码:

/**
	1.绑定到主视图
	2.通过bind属性绑定到具体的子视图
8*/
Ext.define('Admin.view.order.OrderViewModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.orderViewModel',
	stores: {
        orderLists: {
            type: 'orderStore',//Store reference ==Store的属性 alias: 'store.orderStore',		
            autoLoad: true //Auto load
        }
    }
});
//在主视图中,Grid.js
bind:'{orderLists}',

six:表格上面需要一个查询表单 — — OrderGridQueryForm.js

seven:查询触发函数,因此需要一个控制器 — — OrderViewController.js

eight:添加数据需要弹出一个外框组件 — —  OrderGridWindow.js

nine :弹窗组件中放入一个 表单 — — OrderGridForm.js

综上所述:九个步骤即可开发出这个模块

四、开发代码 ~~ 以下采用MVVM分层的开发模式。

first:订单模块主视图,因此需要一个主视图组件。— — Order.js

/**
*订单模块主视图
	在主视图配置viewController和viewModel,
	那么主视图的子视图也可以访问配置好viewController和viewModel。
*/
Ext.define('Admin.view.order.Order', {		//1.修改文件路径
      extend: 'Ext.container.Container',	//2.继承的组件类型
	//3.重写继承组件的属性:
    xtype: 'order',
    
    //这是第seven步骤开发的控制器,通过以下的开发进行绑定
    controller: 'orderViewController',	//视图绑定viewController
    
    
    //这是第five步骤开发的ViewModel,将viewModel绑定到主视图
    viewModel : {type: 'orderViewModel'},	//视图绑定viewModel
	

    layout:'fit',
    margin: '20 20 20 20',
    items: [{
        //这是第二步骤开发的表格组件
		xtype: 'orderGrid'
	}]
});

second:主视图中,放一个表格,因此需要一个表格组件。表格的column跟bbar跟Grid写在一个文件中 — — OrderGrid.js

/**
*订单模块子视图
*/
Ext.define('Admin.view.order.OrderGrid', {		//1.修改文件路径
      extend: 'Ext.grid.Panel',					//2.继承的组件类型
	//3.重写继承组件的属性:
    xtype: 'orderGrid',
	id:'orderGrid',
	title:'<b>订单列表</b>',
	selModel: Ext.create('Ext.selection.CheckboxModel'),
	bind:'{orderLists}',
	dockedItems: [{
        xtype: 'toolbar',
        dock: 'top',
        items: [Ext.apply({xtype: 'orderGridQueryForm'})]
    }],
	
  
	columns: [
		{text: 'ID'			  ,sortable:true ,dataIndex:'orderId',hidden:true},
        {text: '订单编号' ,sortable:true ,dataIndex:'orderNumber' ,width:100},
		{text: '创建时间'  ,sortable:true ,dataIndex:'createTime'  ,width:160
			,renderer: Ext.util.Format.dateRenderer('Y/m/d H:i:s')},
		{text: '优先级',sortable:true ,dataIndex:'level'    ,width:75},
		{text: '供应商ID'			  ,sortable:true ,dataIndex:'supplierId',hidden:true},
		{text: '供应商名称' ,sortable:false ,dataIndex:'suppName' ,width:120},
		{text: '商品编号' ,sortable:false ,dataIndex:'pid' ,hidden:true},
		{text: '商品名称' ,sortable:false ,dataIndex:'pname' ,width:120},
		{text: '商品数量' ,sortable:true ,dataIndex:'pnumber' ,width:75},
		{text: '商品单价' ,sortable:false ,dataIndex:'cost' ,width:75},
		{text: '总价格'		  ,sortable:false ,dataIndex:'totalPrice' ,width:130},
			{text: '是否入库'		  ,sortable:false ,dataIndex:'isIncomeStyle' ,flex:1}
	],
// paging bar on the bottom
	bbar: Ext.create('Ext.PagingToolbar', {
		bind:'{orderLists}',
		displayInfo: true,
		displayMsg: 'Displaying topics {0} - {1} of {2}',
        emptyMsg: "No topics to display",
		items:['-', {
			//xtype:'button',
			text: '新建订单',
			iconCls: 'x-fa fa-plus',
			handler: 'orderGridOpenAddWindow'//绑定OrderViewController中的事件
		}, 
		{
			//xtype:'button',
			text: '修改订单',
			iconCls: 'x-fa fa-undo',
			handler: 'orderGridOpenEditWindow'//绑定OrderViewController中的事件
		}
		, 
		{
			//xtype:'button',
			text: '删除订单',
			iconCls: 'x-fa fa-trash ',
			handler: 'orderGridDeleteDate'//绑定OrderViewController中的事件
		}, 
		{
			//xtype:'button',
			text: '导出订单',
			iconCls: 'fa fa-share-alt',
			handler: 'orderGridExportXls'//绑定OrderViewController中的事件
		}]
	})	 
});

third:GrId需要显示数据,因此需要一个数据源Store --  OrderStore.js 

Ext.define('Admin.store.order.OrderStore', {
    extend: 'Ext.data.Store',

    alias: 'store.orderStore',			  //1.Store取别名(reference)

    model: 'Admin.model.order.OrderModel',//2.设置model的全路径


	//data:{
	//	'items':[
	//		{ 'orderId': 1, 'orderNumber': 'od0000001',  "createTime":"2017/9/8 16:35:00",  "level":"高" ,  'goodsId'	:10001,  'goodsName'	:'A',  'goodsNum'	:'50',  'goodsPrice'	:220,  'totalPrice'	:1100},
	//		{ 'orderId': 2, 'orderNumber': 'od0000002',  "createTime":"2017/9/8 16:35:00",  "level":"中",  'goodsId'	:10002,  'goodsName'	:'B',  'goodsNum'	:'40',  'goodsPrice'	:50,  'totalPrice'	:2000},
	//		{ 'orderId': 3, 'orderNumber': 'od0000099', "createTime":"2017/9/8 16:35:00",  "level":"中" ,  'goodsId'	:10003,  'goodsName'	:'C',  'goodsNum'	:'30',  'goodsPrice'	:150,  'totalPrice'	:4500},
	//	{ 'orderId': 4, 'orderNumber': 'od0000100', "createTime":"2017/9/8 16:35:00", "level":"低"  ,  'goodsId'	:10004,  'goodsName'	:'D',  'goodsNum'	:'20',  'goodsPrice'	:100,  'totalPrice'	:2200}
	//	]
	//},
	proxy: {
		//type: 'memory',
		type: 'ajax',
		url: 'order/findPage.json',
		reader: {
			type: 'json',
			//rootProperty: 'items'
			rootProperty: 'content',
			totalProperty: 'totalElements'	
		},
		simpleSortMode:true
	},
	pageSize: 14,
	autoLoad: true,
	remoteSort: true,//全局排序

    sorters: {
        direction: 'DESC',
        property: 'createTime'
    }
});

four:而我们的Store数据源需要一个Model — — OrderModel.js.

Ext.define('Admin.model.order.OrderModel', {
    extend: 'Admin.model.Base',
    fields: [
		{name:'orderId'			,type: 'int'},
        {name:'orderNumber' ,type: 'string'},
        {name:'createTime'	,type: 'date'},
		{name:'level'		,type: 'string'},
			{name:'supplierId'		,type: 'int'},
		{name:'suppName'		,type: 'string'},
//		{name:'goodsId'			,type: 'int'},
//		{name:'goodsName'		,type: 'string'},
     	{name:'pnumber'		,type: 'int'},
//		{name:'goodsPrice'		,type: 'float'},
		{name:'totalPrice'		,type: 'float'}
    ]
});

five:这一步很重要,就是将我们的数据绑定到ViewModel上,

/**
	1.绑定到主视图
	2.通过bind属性绑定到具体的子视图
8*/
Ext.define('Admin.view.order.OrderViewModel', {
    extend: 'Ext.app.ViewModel',
    alias: 'viewmodel.orderViewModel',
	stores: {
        orderLists: {
            type: 'orderStore',//Store reference ==Store的属性 alias: 'store.orderStore',		
            autoLoad: true //Auto load
        }
    }
});

six:表格上面需要一个查询表单 — — OrderGridQueryForm.js


Ext.define('Admin.view.order.OrderGridQueryForm', {
    extend: 'Ext.form.Panel',
    alias: 'widget.orderGridQueryForm',
	id:'myOrderGridQueryForm',
    requires: [
        'Ext.button.Button',
        'Ext.form.field.Text',
        'Ext.form.field.File',
        'Ext.form.field.HtmlEditor',
		'Ext.form.field.TextArea',
		'Ext.form.field.Time',
		'Ext.form.field.ComboBox',
		'Ext.form.field.Date',
		'Ext.form.field.Radio',
		'Ext.form.field.Hidden'
    ],
    //viewModel: {type: 'emailcompose'},
    //cls: 'email-compose',
	controller: 'orderViewController',
    layout: {
        type:'hbox',
        align:'stretchmax'
		
    
    },

    bodyPadding: 0,
    scrollable: false,

    defaults: {
        labelWidth: 65,
        labelSeparator: ':'
    },
    items:[{
		xtype: 'textfield',
		fieldLabel: '订单编号',
		reference: 'orderSearchForm-orderNumber',
		name:'orderNumber',
		//allowBlank : false,
		padding:4
	},{
		xtype: 'datefield',
		format: 'Y/m/d H:i:s',
		fieldLabel: '创建时间',
		editable:true,
		reference: 'orderSearchForm-createTime',
		name:'createTime',
	
		
		padding:4
		,width:250
	},{ 
		xtype: 'combobox',
		reference: 'orderSearchForm-isIncome',
		editable:false,
		fieldLabel: '是否入库',
		name:'isIncome',
		
		padding:4,
		store:  Ext.create('Ext.data.Store', {
			fields: ['value', 'name'],
			data : [
				{"value":1, 	"name":"已入库"},
				{"value":0,     "name":"未入库"}
			]
		}),
		queryMode: 	  'local',
		displayField: 'name',
		valueField:   'value'
	},{ 
		xtype: 'combobox',
		reference: 'orderSearchForm-level',
		editable:false,
		fieldLabel: '优先级',
		name:'level',
		
		padding:4,
		store:  Ext.create('Ext.data.Store', {
			fields: ['value', 'name'],
			data : [
				{"value":"HIGH", 	"name":"高"},
				{"value":"MEDIUM",  "name":"中"},
				{"value":"LOW", 	"name":"低"}
			]
		}),
		queryMode: 	  'local',
		displayField: 'name',
		valueField:   'value'
	},{  
			xtype: 'button',
		    ui: 'gray',
			margin:5,
			//align:'right',
			iconCls: 'x-fa fa-search',
			text: '查询',
			handler: 'orderSearchFormSubmit'
		}
		,{  
			xtype: 'button',
		    ui: 'gray',
			margin:5,
			//align:'right',
			iconCls: 'x-fa fa-search',
			text: '置空',
			handler: function() {
                this.up('form').getForm().reset();
				var store = this.up('gridpanel').getStore();
				 Ext.getCmp('orderGrid').store.getProxy().extraParams ={
					 
				 };
                store.reload();
			    //Ext.getCmp('orderGrid').store.getProxy().url = 'order/findPage'; 
	
            }

		}
	],
	//,{
	//	xtype: 'textfield',
	//	fieldLabel: 'GoodsId',
	//	name:'goodsId'
    //}
	//,{
	//	xtype: 'textfield',
	//	fieldLabel: 'GoodsName',
	//	name:'goodsName'
    //}
	//,{
	//	xtype: 'textfield',
	//	fieldLabel: 'GoodsNum',
	//	name:'goodsNum'
    //}
	//,{
	//	xtype: 'textfield',
	//	fieldLabel: 'GoodsPrice',
	//	name:'goodsPrice'
    //}

   /* bbar: {
        items: ['->',{
			xtype: 'button',
			//ui: 'soft-red',
			text: '查询',
			handler: ''
		},{
			xtype: 'button',
			//ui: 'gray',
			text: '取消',
			handler: ''
		}]
    }
	*/
});

seven:查询触发函数,因此需要一个控制器  CURD代码都在里面— — OrderViewController.js

Ext.define('Admin.view.order.OrderViewController', {
    extend: 'Ext.app.ViewController',
    alias: 'controller.orderViewController',

     orderGridOpenAddWindow: function(btn) {
	
		var cfg = Ext.apply({
			xtype: 'orderGridWindow',
		items: [Ext.apply({xtype: 'orderGridForm'})]
		},{
			title:'创建订单'//,width: 800//,height: 600
		});
		
		Ext.create(cfg);		
    },
	orderGridFormSubmit: function(btn) {
		var orderGridForm = btn.up('form').getForm();
		var win = btn.up('window');
	
		
		orderGridForm.submit( { 
			//waitTitle : '请稍后...', 
			//waitMsg : '正在保存订单信息,请稍后...', 
			url : 'order/saveOrUpdate', 
			method : 'post', 
			success : function(form, action) { 
				Ext.Msg.alert("提示",action.result.msg); 
				win.close();
				//必须OrderGrid中增加对应id:'orderGrid'属性
				Ext.getCmp('orderGrid').store.reload();			
				}, 
			failure : function(form, action) { 
			    win.close();
				Ext.Msg.alert("提示",action.result.msg); 
				
			} 
        });
    },
	orderGridWindowClose: function(btn) {
		var win = btn.up('window');
		if(win){
			win.close();
		}
    },
	orderGridOpenEditWindow: function(btn) {
		var grid = btn.up('gridpanel');//获取Grid视图
		var selModel = grid.getSelectionModel();//获取Grid的SelectionModel
        if (selModel.hasSelection()) {//判断是否选中记录
           var record = selModel.getSelection()[0];//获取选中的第一条记录
           //创建修改window和form
		   var orderGridWindow = Ext.widget('orderGridWindow',{
				title:'修改订单',
				items: [{xtype: 'orderGridForm'}]
			});
		   		//让form加载选中记录
           orderGridWindow.down("form").getForm().loadRecord(record);
        }else{
        	Ext.Msg.alert('提示',"请选择一行数据进行修改!");
        }
    },
	orderGridDeleteDate: function(btn) {
		var grid = btn.up('gridpanel');
		var selModel = grid.getSelectionModel();
        if (selModel.hasSelection()) {
            Ext.Msg.confirm("警告", "确定要删除吗?", function (button) {
                if (button == "yes") {
                    var selected = selModel.getSelection();
                    var selectIds = []; //要删除的id
                    Ext.each(selected, function (record) {
						//alert(record.data.orderId);
                        selectIds.push(record.data.orderId);
                    })
					
                  	Ext.Ajax.request({ 
						url : 'order/deleteByIds', 
						method : 'post', 
						params : { 
							ids:selectIds
						}, 
						success: function(response, options) {
			                var json = Ext.util.JSON.decode(response.responseText);
				            if(json.success){
				            	Ext.Msg.alert('操作成功', json.msg);
				                grid.getStore().reload();
					        }else{
					        	Ext.Msg.alert('操作失败', json.msg);
					        }
			            }
					});

                }
            });
		}else{
        	Ext.Msg.alert('提示',"请选择一行数据进行删除!");
        }
    },
	orderSearchFormSubmit:function(btn){
		
		var store = btn.up('gridpanel').getStore();
	
		//2.按照所选字段进行查询参数(条件)的扩展
		   var formValues=btn.up('form').getForm().getValues();
               // alert(formValues["createTime"]);
               // alert(formValues["orderNumber"]);
                //alert(formValues["level"]);
				
		 if (formValues["createTime"]==''&&formValues["orderNumber"]==''&&formValues["level"]==''&&formValues["isIncome"]==''&&formValues["isIncome"]!=0) {		 
			 store.getProxy().extraParams ={ };
			 store.reload();			
		 }else if(formValues["createTime"]==''){
			 store.getProxy().extraParams ={ };
			 //alert('createTime kong');
			 	//1.清空所有查询条件
			Ext.apply(store.proxy.extraParams, {
				orderNumber:'',
				//createTime:'',
				level:'',
				isIncome:''
			}); 
			 Ext.apply(store.proxy.extraParams, {
					orderNumber:this.lookupReference('orderSearchForm-orderNumber').getValue(),
					level:this.lookupReference('orderSearchForm-level').getValue(),
					isIncome:this.lookupReference('orderSearchForm-isIncome').getValue()
					//createTime:null,					
				});  
				store.load({params: {start:0,limit:14,page:1}});
			 
		 }else{
			 	//1.清空所有查询条件
			Ext.apply(store.proxy.extraParams, {
				orderNumber:'',
				createTime:'',
				level:'',
				isIncome:''
			}); 
			  Ext.apply(store.proxy.extraParams, {
					orderNumber:this.lookupReference('orderSearchForm-orderNumber').getValue(),
					level:this.lookupReference('orderSearchForm-level').getValue(),
					createTime:Ext.util.Format.date(this.lookupReference('orderSearchForm-createTime').getValue(), 'Y/m/d H:i:s'),					
				    isIncome:this.lookupReference('orderSearchForm-isIncome').getValue()
				});  
				store.load({params: {start:0,limit:14,page:1}});
			  
		 }
		//alert(store.proxy.extraParams.createTime);
        		
			 
	        //myOrderGridQueryForm.form.reset();
         
		
		  //btn.up('gridpanel').reset();
	}
	,
	orderGridExportXls:function(){
		//alert("导出功能");
		/* Ext.Ajax.request( {
			  url : 'order/excel/export',
			  method : 'get',
			  success : function(response, options) {
				 var json = Ext.util.JSON.decode(response.responseText);
				            if(json.success){
				            	Ext.Msg.alert('操作成功', json.msg);
				                grid.getStore().reload();
							}else{
					        	Ext.Msg.alert('操作失败', json.msg);
							}
			  },
			  failure : function() {
				  alert("failse");
			  }
		});*/
		window.location.href = "order/excel/export";
		
		
	}



});

eight:添加数据需要弹出一个外框组件 — —  OrderGridWindow.js

Ext.define('Admin.view.order.OrderGridWindow', {
    extend: 'Ext.window.Window',
    alias: 'widget.orderGridWindow',
    autoShow: true,
    modal: true,

    layout: 'fit',

   // width: 200,
    //height: 200,

    afterRender: function () {
        var me = this;

        me.callParent(arguments);

        me.syncSize();

        // Since we want to always be a %age of the viewport, we have to watch for
        // resize events.
        Ext.on(me.resizeListeners = {
            resize: me.onViewportResize,
            scope: me,
            buffer: 50
        });
    },

    doDestroy: function () {
        Ext.un(this.resizeListeners);

        this.callParent();
    },

    onViewportResize: function () {
        this.syncSize();
    },

    syncSize: function () {
        var width = Ext.Element.getViewportWidth(),
            height = Ext.Element.getViewportHeight();

        // We use percentage sizes so we'll never overflow the screen (potentially
        // clipping buttons and locking the user in to the dialog).

        this.setSize(Math.floor(width * 0.36), Math.floor(height * 0.52));
        this.setXY([ Math.floor(width * 0.05), Math.floor(height * 0.05) ]);
    }
});

nine :弹窗组件中放入一个 表单 — — OrderGridForm.js

Ext.define('Admin.view.order.OrderGridForm', {
    extend: 'Ext.form.Panel',
    alias: 'widget.orderGridForm',
    requires: [
        'Ext.button.Button',
        'Ext.form.field.Text',
        'Ext.form.field.File',
        'Ext.form.field.HtmlEditor',
		'Ext.form.field.TextArea',
		'Ext.form.field.Time',
		'Ext.form.field.ComboBox',
		'Ext.form.field.Date',
		'Ext.form.field.Radio',
		'Ext.form.field.Hidden'
    ],
    //viewModel: {type: 'emailcompose'},
    //cls: 'email-compose',
	controller: 'orderViewController',
    layout: {
        type:'vbox',
        align:'stretch'
    },

    bodyPadding: 10,
    scrollable: true,

    defaults: {
        labelWidth: 75,
        labelSeparator: ':'
    },
    items: [{
		xtype: 'hidden',
		fieldLabel: 'Id',
	
		name:'orderId'
	},
	{
		xtype: 'hidden',
		fieldLabel: 'isIncome',
	
		name:'isIncome'
	},{
		xtype: 'textfield',
		fieldLabel: '订单编号',
		blankText:'订单编号不能为空',
		allowBlank: false,
		name:'orderNumber'
		

	},{
		xtype: 'datefield',
		format: 'Y/m/d H:i:s',
		fieldLabel: '创建时间',
		blankText:'创建时间不能为空',
		editable:true,
		allowBlank: false,
		name:'createTime'
	},{
		xtype: 'combobox',
		fieldLabel: '订单优先级',
		blankText:'优先级不能为空',
		editable:false,
		allowBlank: false,
		name:'level',
		store:  Ext.create('Ext.data.Store', {
			
			fields: ['value', 'name'],
			data : [
				{"value":"HIGH", 	"name":"高"},
				{"value":"MEDIUM",  "name":"中"},
				{"value":"LOW", 	"name":"低"}
			]
		}),
		queryMode: 	  'local',
		displayField: 'name',
		valueField:   'value'
	},
	{
		xtype: 'combobox',
		fieldLabel: '选择供应商',
		blankText:'供应商不能为空',
		
		allowBlank: false,
		name:'supplierId',
		store: {
			type:'orderShowSupplierStore'
		},
		queryMode: 	  'local',
		displayField: 'suppName',
		valueField:   'id',
		
		listeners:{
			expand: function(combo, record, index) {
			combo.store.reload();
			
			}
		}
		
	}
	,
	{
		xtype: 'combobox',
		fieldLabel: '选择商品',
		blankText:'商品名称不能为空',
		
		allowBlank: false,
		name:'pid',
		store: {
			type:'orderShowProductStore'
		},
		queryMode: 	  'local',
		displayField: 'pname',
		valueField:   'pid',
		
		listeners:{
			expand: function(combo, record, index) {
			combo.store.reload();

			}
		}
		
	}
	/*,{
		xtype: 'combobox',
		fieldLabel: '选择商品',
		editable:false,
		allowBlank: false,
		name:'pid',
		store:  Ext.create('Ext.data.Store', {
			
			fields: ['value', 'name'],
			data : [
				{"value":"11", 	"name":"小郑"},
				{"value":"12",  "name":"林霞"},
				{"value":"13", 	"name":"zz"},
				{"value":"14", 	"name":"欣欣"},
				{"value":"15", 	"name":"小杰"},
				
			]
		}),
		queryMode: 	  'local',
		displayField: 'name',
		valueField:   'value'
	}
	*/
	
	,{
		xtype: 'textfield',
		fieldLabel: '产品数量',
	    regex:/^[0-9]*[1-9][0-9]*$/,
        regexText:'请输入正整数',
		blankText:'数量不能为空',
		
		allowBlank: false,
		name:'pnumber'
    }],
    bbar: {
        items: ['->',{
			xtype: 'button',
			//ui: 'soft-red',
			text: '保存',
			handler: 'orderGridFormSubmit'
		},{
			xtype: 'button',
			//ui: 'gray',
			text: '取消',
			handler: 'orderGridWindowClose'
		}]
    }
});

综上所述:九个步骤即可开发出这个模块

五、代码分享

https://download.csdn.net/download/xiaozhegaa/10692082 

猜你喜欢

转载自blog.csdn.net/xiaozhegaa/article/details/82874695
今日推荐