• 工作笔记


    主要是记录自己工作时候用到过的一些东西吧,算是学习积累了。

    1.这是在平台修改的第一个bug,是ExtJS显示方面的问题。

    问题情况:ExtJS的gird布局时,鼠标滑过弹出提示框,提示框内容超出,导致数据显示不全。

    解决:让框内的数据如果太多超出页面就自动换行

    主要代码:

     {
                header: F.AL.EventName,
                dataIndex: "Content",
                 480,
                renderer: function (val, meta, rec) {
                    if (typeof rec.data.Code != 'undefined') {
                        var code = Fleet.util.decimalToHex(rec.data.Code);
                        //var code = rec.data.EventCode;
                        var htmlValue = "<div style='word-wrap:break-word;word-break:break-all;overflow:auto;'>" + val + "</div>" //自动换行
                        meta.tdAttr = 'data-qtip="[' + code + ']' + htmlValue + '"';
                        if (Ext.isEmpty(val)) {
                            return '[' + code + ']';
                        }
                    }
                    return '<div class="e_' + code + '" style="display:inline;padding:3px 0 0 20px;height:18px;"></div>' + val;
                }
            }
    View Code

    主要就是下面这两句了。把后台传出来的长数据加上style='word-wrap:break-word;word-break:break-all;overflow:auto;'浏览器懂的换行语句组装起来,然后再添加到弹出提示框 'data-qtip="[' + code + ']' + htmlValue + '"';里面。

    var htmlValue = "<div style='word-wrap:break-word;word-break:break-all;overflow:auto;'>" + val + "</div>" //自动换行
    meta.tdAttr = 'data-qtip="[' + code + ']' + htmlValue + '"'; 

    2.ExtJs框架中日期函数用法

    参考资料:http://www.jb51.net/article/84764.htm

    我这边的需求是把类似这样的“Wed Oct 31 2012 14:57:59 GMT+0800 (中国标准时间)”显示规范成2012/8/31这样。

    我的需求是最终时间截点默认是当天时间,var initEndDTStr = Ext.Date.format(new Date(), "Y/m/d");

    初始日期我默认设置为七天前,所以这样写,

    var initBeginDT = Ext.Date.add(new Date(initEndDTStr), Ext.Date.DAY, -7);  //获取距离今天早7天前(即一个星期),如果是距离今天7天后就“+7”,如果是一个月就写var initBeginDT = Ext.Date.add(new Date(initEndDTStr), Ext.Date.MONTH, -1);这样你就可以获取一个前的时间了。其它的具体参考我上面给的参考资料
    var initBeginDTStr = Ext.Date.format(initBeginDT, "Y/m/d");   //规范化刚刚上面获取的时间

    最后一句是用来计算日期的代码,把时间折换以后再进行运算。

    var day = Math.round(new Date(initEndDTStr).getTime() - new Date(initBeginDTStr).getTime()) / (86400000);    //这句主要是用在你日期换算上,当然像上面那样你还要算什么,我是说如果你拿到两个时间点,你就可以参考这个方法了。具体看你的要求。

            var initEndDTStr = Ext.Date.format(new Date(), "Y/m/d");
            var initBeginDT = Ext.Date.add(new Date(initEndDTStr), Ext.Date.DAY, -7);
            var initBeginDTStr = Ext.Date.format(initBeginDT, "Y/m/d");
            var day = Math.round(new Date(initEndDTStr).getTime() - new Date(initBeginDTStr).getTime()) / (86400000);
    View Code

    3.ExtJS中Combo二级联动

    参考资料:http://www.cnblogs.com/iamlilinfeng/archive/2012/06/23/2559532.html

    我这里的需求是做一个省市二级联动。大概的样子是这样的,如图。我的分组选择“GuangdongProvince",然后我的子分组就自动将广东省里面有的”阳江市“、”ZhuhaiCity"等列出来。(因为涉及到公司项目来的,就截个大概样图

    我的代码大概是这样的,是用的ExtJS框架哈

                items: [{
                    labelWidth: F.EN ? 65 : 60,
                    margin: 3,
                    name: 'province',
                    fieldLabel: F.DM.Province,
                    xtype: 'combo',
                    oldValue: '',
                    queryMode: 'local',
                    triggerAction: 'all',
                    editable: false,
                    displayField: 'text',
                    valueField: 'value',
                    maxLength: 150,
                     110,
                    store: new Ext.data.ArrayStore({
                        fields: ["value", "text"],
                        data: proStoreData,                    
                    }),
                    listeners: {
                        change: function () {
                            var txtProvince = pnlSearch.down('[name=province]');
                            var txtCity = pnlSearch.down('[name=city]');
                            var province = txtProvince.getValue();
                            txtCity.setValue('');
                            txtCity.store.load({
                                params: {
                                    groupId: province
                                }
                            });
                        }
                    }
                }, {
                    labelWidth: F.EN ? 65 : 60,
                    name: 'city',
                    id: 'city',
                    margin: 3,
                    fieldLabel: F.DM.City,
                    xtype: 'combo',
                    oldValue: '',
                    queryMode: 'local',
                    triggerAction: 'all',
                    editable: false,
                    displayField: 'text',
                    valueField: 'value',
                    maxLength: 150,
                     110,
                    store: Ext.data.ArrayStore({
                        storeId: 'city',
                        fields: ['value', 'text'],
                        proxy: {
                            type: 'ajax',
                            url: '/TestConfig/GetGroupChildInfo',
                            params: { groupId: '' },
                            reader: {
                                type: 'json',
                            }
                        },
                       
                    }),
    
                }]
    View Code

    核心部分分析

    listeners: {                                         //在分组的combo下添加一个监听事件
    change: function () {                        //如果我的分组进行更改了
    var txtProvince = pnlSearch.down('[name=province]');      //定义一个txtProvince来装获取到我的分组(即name=province)这个面板。
    var txtCity = pnlSearch.down('[name=city]');                     //定义一个txtCity 来装获取到我的子分组(即name=city)这个面板。
    var province = txtProvince.getValue();                             //定义一个province来获取我的分组面板中用户选好的值。
    txtCity.setValue('');                                                          //匹对前先清空我的子分组面板(每次都重新选了province,我的city都会清空一次
    txtCity.store.load({                                                         //加载我的city数据
    params: {
    groupId: province                                                         //数据加载的参数条件值就是我选的province值来决定
    }
    });
    }
    }

    上面的方法跟我提供的参考资料里面的写法稍微有些出入,不过原理都是一样的。主要是为跟我一样的小小白提供一个写法和具体思路。

     4.FormPanel.getForm().getValues()的用法参考。

    参考资料:http://blog.csdn.net/phker/article/details/8496252

    我遇到这个问题的时候情况还比较复杂,讲一下可能对跟我一样不会处理跨组件取值的朋友提供一个灵感。

    情景:我在我的Panel面板里做一个高级按钮的功能,,点击这个按钮出现window组件内容。大概组件层级就是Panel层包含window。图例

    我的panel里面的代码(部分,只是为了方便理解,节选了部分

    var pnlButton = Ext.create('Ext.form.Panel', {
    items: [{
                         80,
                        text: F.C.Advanced,
                        id: 'advanced1',
                        xtype: 'button',
                        name: 'btnAdvanced',
                        style: 'margin-top:3px',
                        iconCls: 'bullet_tick_gray',
                        handler: function () {
                            var win = me.advanceWin;
                            if (!win) {
                                win = Ext.create('Fleet5.DeviceManage.AdvancedWin', {
                                    allowNonSelected: true,
                                });
                                me.advanceWin = win;
                            }
                            win.show();
                        }
                    }]
    });
    
    
    
    //我有一个方法要获取window的值
        getCondition: function () {
            var me = this;     
    
            if (me.advanceWin) {
                var value = me.advanceWin.down('form').form.getValues();   //获取到window里面所有的值
                
            }
            
            return value;
            
        },
    SearchPanel.js

    我的window的代码(部分

    var frmAdvance = Ext.create('Ext.form.Panel', {
    items: [{
                        xtype: 'checkbox',
                        columnWidth:0.6,
                        id: 'cbxTestTime',
                        name: 'testTimeAbled',
                        inputValue:'1',
                        listeners: {
                            change: function (checkbox, checked) {
                                if (checked) {
                                    Ext.getCmp('txtTestTime').setDisabled(false);                                
                                    var testTime = Ext.getCmp('txtTestTime');
                                    testTime.focus(testTime);
                                } else {
                                    Ext.getCmp('txtTestTime').setDisabled(true);
                                }
                            }
                        }
                    }, {
                        xtype: 'label',
                        text: F.DM.TestTime + '>='
                    }, {
                        xtype: 'numberfield',
                        id: 'txtTestTime',
                        name: 'testTime',
                        disabled: true,
                        allowBlank:false,
                        value: 20
                    }, {
                        xtype: 'label',
                        text: '(' + F.DM.Minute + ')'
                    }]
                }
    });
    AdvancedWin.js

    这里要注意了,你如果是取来做参数用,window里面的item的name:'testTimeAbled',这个testTimeAbled就是我后台要的参数的名字,因为你用getValues()方法的时候,它自动获取了你name的内容和你的value的。详细的理解看我什么提供的参考资料,那里的案例一看就明白了。很清晰。

    5.ExtJS中不同组件之间如何互相调用,取值。

    参考资料:http://blog.csdn.net/cswhale/article/details/18404731

    ExtJS从旧版本例如3.x迁到5.x,这时候最大的不同就是组件和方法拆分,但这一拆分呢,数据的传递就成为维系各个组件间最重要的东西。组件之间若即若离,层层包含,上下级组件之间相互调用的方法主要是用up、down。详细请认真阅读我提供的参考资料,里面其实讲得算详细了。不过我这里补充一下。

    比如我的两个文件,我的最底层的大容器是DeviceStateList.js这个gridPanel,我这里又包含了一个DSListSearchPanel.js的formPanel。现在呢,我的gridPanel里面要formPanel里面的一个值调出来用。

    解决方案①:

    你取值的时候就可以这样用了。

    解决方案②:这个就比较麻烦了,先在DeviceStateList.js这个gridPanel里面获取到DSListSearchPanel.js的formPanel来,然后在这个formPanel下取值。核心代码部分:

    var searchPanel = this.down('dslistsearchpanel');    //先获取到DSListSearchPanel.js的formPanel,这里的dslistsearchpanel是我DSListSearchPanel.js的别名,如截图

    var initBeginDTStr = searchPanel .down('[name=hidBeginDT]').getValue(); //这样你也可以获取到DSListSearchPanel.js下的值啦

    6.ExtJS中布局的讲解

    参考资料:http://www.cnblogs.com/yqskj/archive/2012/10/25/2738425.html

    ①layout:‘form’如何实现内容的对齐

    var fieldWidth = F.EN ? 95 : 85;
        var txtWidth = 200;
        //新建组win
        var frmNewGroup = new Ext.form.FormPanel({
            id: 'frmNewGroup',
            projectType: projectType,
            plain: true,
            bodyStyle: "padding:10px 0px 0px 0px;",
            isEdit: false,
            labelAlign: 'right',
            layout: 'form',
            border: false,
            autoWidth: true,
            labelWidth: fieldWidth,  //这里控制位置
            items: [{
                xtype: 'textfield',
                fieldLabel: F.DMG.Name,
                ref: 'projectName',
                name: 'Name',
                maxLength: 50,
                vtype: 'group',
                value: '',
                 txtWidth,
                allowBlank: false
            }, {
                xtype: 'textarea',
                value: '',
                ref: 'desc',
                maxLength: 200,
                vtype: 'commonChar',
                 txtWidth,
                name: 'Desc',
                fieldLabel: F.DMG.Desc,
                height: 100
            }, {
                xtype: 'displayfield',
                id: 'newError',
                name: 'ErrorMsg',
                labelAlign: 'right',
                hideLabel: true,
                 txtWidth,
                value: ''
            }]
        });
    form

    7.visualStudio比较有用的快捷键

    ①代码整理对齐:ctrl+k+f

    sumblimeText快捷键:http://blog.csdn.net/qq_35620807/article/details/51933831

    html5自动补充:

    ①.Ctrl + N,新建一个文档;

    ②.Ctrl + Shift + P,打开命令模式,再输入 sshtml 进行模糊匹配,将语法切换到html模式;

    ③.输入  !,再按下 Tab键或者 Ctrl + E ,就能快速打开HTML5的整体结构。

    8.ExtJS刷新你的columns和store

    我们都知道我们在查询的时候,数据是按需加载即store.load(),那么如果不幸的你跟我一样你的columns也面临根据条件要进行一次刷新,那么是怎么写呢。

    核心代码:

     items: [{
                         80,
                        text: F.C.Search,
                        xtype: 'button',
                        name: 'btnSearch',
                        style: 'margin-top:3px',
                        iconCls: 'search',
                        handler: function () {
                            var grid = searchPanel.up('#gridItem'); //选择grid组件(这里searchPanel是被grid包含的,所以是up(''),#gridItem是grid的id)
                            if (grid) {
                                var cols = grid.getColumns();//获取到要更新的columns,getColumns()是grid中的一个方法
                                grid.reconfigure(grid.store, cols);//重新配置grid中的store和cols,即实现store和columns的刷新。
                                grid.store.currentPage = 1;
                                grid.store.load();
                            }
                        }
                    }]
    View Code

     9.如何快速定位到bug文件进而锁定bug位

    情景:项目后台是asp.net的MVC架构,前端是Ext.js。

    对于我,只是对java的MVC有一点理解,可以写简单的增删改查C#代码。所以这样比较复杂的情况我一开始是懵逼的,毕竟我只是个刚刚踏入社会的宝宝吖。一开始是我的亲导师儿会告诉我文件在哪里,然后我直接去找bug位就好了。因为这个项目已经很有历史的样子,所以非常臃肿庞大,我也是现在才掌握如何快速找到问题位的方法的。记录下来、下次忘记了可以提醒一下。节约时间。

    如果是前端问题,浏览器F12,NetWork,XHR,看js文件,根据js文件位置找到文件、然后定位bug位,修改。

    如果是后台问题,先从前端确定,逆推回后台url,找到controller里面对应的处理数据方法,主要方法是断点、看数据的流向,判断问题点。

     10.Ext4以上如何引用外部插件。

    1.在.aspx中进行javascriptLoad('');

    2.在.js文件中(就是你要引用的Extjs文件)require

    10.combo组件的模糊搜索

    var comb = Ext.create('Ext.form.ComboBox', {
    fieldLabel: F.Conf.AlarmContent,
    labelWidth: F.EN ? 118 : 58,
    itemId: 'comboAlarmContent',
    store: alarmContentStore,
    queryMode: 'local',
    editable: true,
    fuzzyQuery: true,
    multiSelect: false,
    displayField: 'text',
    labelAlign: 'right',
    valueField: 'value',
    value: '',
    F.EN ? 250 : 228
    });

    11.英文资源:F.EN ? 168 :68

    12.拼接搜索条件

    var sql = @"
    select
    Id,
    AlarmLevel,
    AlarmContent,
    alarmType,
    AlarmContentZH,
    IsDisplay,
    EmailEnable,
    SMSEnable
    from AlarmCodeInfo
    {0}
    ";

    string whereSql = string.Empty;
    var wheres = new List<string>();

    if (!string.IsNullOrEmpty(alarmContent))
    {
    wheres.Add(string.Format(" ( [AlarmContentZh] = '{0}' or [AlarmContent] = '{0}' )",
    alarmContent));

    }

    if (alarmType != null)
    {
    wheres.Add(string.Format(" ( [alarmType] = '{0}' )", alarmType));
    }

    if (alarmLevel != null)
    {
    wheres.Add(string.Format("( [AlarmLevel] = '{0}' )", alarmLevel));

    }
    if (wheres.Count > 0)
    whereSql = " WHERE " + string.Join(" AND ", wheres);

    sql = string.Format(sql, whereSql);

    13. double? 是可以null的, 而double是值类型不能为null

    14.js删除数组里某个元素

    for (var n = 0; n < channelList.length; n++) {
    if (v == channelList[n]) {
    channelList.splice(n, 1);
    }
    }

    15.sql规范语句

    新增字段

    /*描述:在[CellInfo]表添加字段[RRUName],用来保存基站的物理小区(RRU)名称
    创建作者:li.liu 2018-4-9
    */
    IF NOT EXISTS(SELECT name AS columnName
    FROM sys.columns
    WHERE object_id = OBJECT_ID('CellInfo') AND name = 'RRUName' )
    BEGIN
    ALTER TABLE [dbo].CellInfo ADD RRUName nvarchar(150) null;
    END
    GO

    新增表

    /*
    描述:创建表RoleAreaInfo
    创建作者:
    修改更新:
    */

    IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[RoleAreaInfo]') AND type in (N'U'))
    BEGIN
    CREATE TABLE [RoleAreaInfo](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [RoleId] [int] NOT NULL,
    [AreaId] [int] NOT NULL,
    CONSTRAINT [PK_RoleAreaInfo] PRIMARY KEY CLUSTERED
    (
    [Id] ASC
    )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
    ) ON [PRIMARY]
    END

    GO

    或者


    /*描述:上海移动KPI表
    创建作者:
    修改更新:
    */
    IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[DHB_Summary]') AND type in (N'U'))
    BEGIN
    CREATE TABLE [dbo].[DHB_Summary](
    [Year] [int] NOT NULL,
    [Month] [int] NOT NULL,
    [ItemClass] [nvarchar](50) NULL,
    [ItemName] [nvarchar](50) NULL,
    [ItemValue] [float] NULL,
    [CreateDT] [datetime] NULL,
    [Creator] [nvarchar](20) NULL
    ) ON [PRIMARY]

    END
    GO

    更新

    UPDATE [ParamConfig]
    SET [GroupCode] = 727
    ,[Name] = 'PUSCH Serving slot Count /s SCell2'
    ,[NetType] = 7
    WHERE Code = 2131108058
    GO

    插入

    IF NOT EXISTS(SELECT 1 FROM ParamConfig WHERE Code=2131111446)
    BEGIN
    INSERT INTO ParamConfig([Code], [GroupCode], [Name], [MinValue], [MaxValue], [Scale], [UnitCode], [EstimateValue], [EstimateType], [EstimateUnitCode], [Alias], [NetType], [IsVisible], [IsStatistic], [ThresholdType], [FilterType], [StatisticType], [PaintSize], [PaintShape], [DefaultColor], [FleetReserved], [AFMonitor], [AFInherit]) VALUES(2131111446, 743, 'DL TBS Average /s', 0, 2147483647, 1, -9999, 0, 0, -9999, 'DL TBS Average/s', 7, 1, 0, 0, 0, 0, 9, 0, 0, 0, 0, 0)
    END
    GO

    16.C#中的sql语句

    select:

    public IList<int> GetByRole(int roleId)
    {
    var sql = @"
    select AreaId as Id from RoleAreaInfo
    where RoleId = :RoleId
    ";
    return Session.CreateSQLQuery(sql)
    .SetInt32("RoleId", roleId)
    .List<int>();
    }

    update:

    public void UpdateReport(int id,string reportView,string reportCondition)
    {

    string sql = @"update SimpleReportTask
    set ReportView = :ReportView, ActualReportCondition =:ActualReportCondition where SimpleReportTaskID=:SimpleReportTaskID";
    IQuery query = Session.CreateSQLQuery(sql)
    .SetString("ReportView", reportView)
    .SetString("ActualReportCondition", reportCondition)
    .SetParameter("SimpleReportTaskID", id);
    int count = query.ExecuteUpdate();
    }

    delete:

    public void DeleteAutoReport(int id)
    {
    var sql = "delete from AutoReportTask where AutoReportTaskId=:a";
    IQuery q = Session.CreateSQLQuery(sql);
    q.SetParameter("a", id);
    q.ExecuteUpdate();
    }

    17.C#下载文件/压缩包

    /// <summary>
    /// 下载报表文件
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [Capability("DownloadReportFile", "下载报表")]
    public ActionResult DownloadReportFile(int id)
    {
    ReportTask report = reportService.GetSimpleReport(id);
    string fullFilePath = report.ReportFile;//absolute
    //string fullFilePathEN = report.ReportFileEN;//absolute
    if (!System.IO.File.Exists(fullFilePath))
    {
    Response.Write(ModelConstants.JS_TIP_FILENOTFOUND);
    }
    else
    {
    if (!report.HasRead)
    {
    //report.HasRead = true;
    //statReportService.UpdateReportTask(report);
    }
    string downFilePath = fullFilePath;
    /* if (ResourceManager.EN && System.IO.File.Exists(fullFilePathEN))
    {
    downFilePath = fullFilePathEN;
    }*/
    FileInfo file = new FileInfo(downFilePath);
    string fileName = string.Format("{0}{1}",
    report.Name, file.Extension);
    DownFileHeader(Response, Request, fileName, file.Length);
    Response.WriteFile(file.FullName);
    }
    Response.End();
    return new EmptyResult();
    }

    /// <summary>
    /// 批量下载报表文件
    /// </summary>
    /// <returns></returns>
    public ActionResult DownloadReportFiles()
    {
    int[] sourceIds = globalService.Request_Get("ids").ToIntArrayOrNull();

    if (sourceIds == null || sourceIds.Length == 0)
    {
    throw new ArgumentNullException("No selected file.");
    }
    string guid = Guid.NewGuid().ToString();
    IList<ReportTask> sourceFiles = reportService.FindStatesByIds(sourceIds);
    DownFileHeader(Response, Request, string.Format("{0}.zip", guid), null);
    var encoding = Encoding.GetEncoding("GBK");
    using (ZipFile zip1 = new ZipFile(encoding))
    {
    foreach (var sourceFile in sourceFiles)
    {
    zip1.AlternateEncoding = encoding;
    zip1.AddFile(sourceFile.ReportFile);
    }
    var cancel = false;
    zip1.SaveProgress += new EventHandler<SaveProgressEventArgs>(
    (object obj, SaveProgressEventArgs args) =>
    {
    if (!Response.IsClientConnected)
    {
    args.Cancel = true;
    cancel = true;
    Response.End();
    }
    });
    Response.Buffer = false;
    Response.BufferOutput = false;
    zip1.Save(Response.OutputStream);
    if (cancel)
    {
    return new CancelResult();
    }
    }
    Response.End();
    return new EmptyResult();
    }

    17.看github源码的工具:https://www.cnblogs.com/12yang-ting/p/7485264.html

    18.xml序列化

    ①重命名List的类名

    [Serializable]
    public class ScannerTestConfig : SimpleTaskConfigBase
    {

    [XmlElement]
    public CWConfig CWConfig;

    }

    [Serializable]
    public class CWConfig
    {
    [XmlElement]
    public decimal? Rate;

    [XmlArrayItem(ElementName = "ChannelInfo")]
    public List<CWChannelInfo> ChannelInfoList;
    }

    [Serializable]
    public class CWChannelInfo
    {
    [XmlElement]
    public string Band;

    }

    效果:

    <ScannerTestConfig>

          <CWConfig>

        <Rate>15</Rate>

        <ChannelInfoList>

          <ChannelInfo>

            <Band>Band1</Band>

          </ChannelInfo>

        </ChannelInfoList>

          </CWConfig>

    </ScannerTestConfig>

    ②缩减一个属性

    [Serializable]

        public class RandomTimeSection 

        {

            [XmlAttribute]

            public bool IsAvailable;

            [XmlElement(ElementName = "SectionInfo")]

            public List<SectionInfo> SectionInfo { get; set; }

        }

    [Serializable]

        public class SectionInfo

        {

            [XmlAttribute]

            public int MinDuration;

            [XmlAttribute]

            public int MaxDuration;

            [XmlAttribute]

            public int Count;

        }

    效果:

    <RandomTimeSection IsAvailable="true">

      <SectionInfo MinDuration="1000" MaxDuration="3000" Count="1" />

      <SectionInfo MinDuration="3000" MaxDuration="5000" Count="1" />

    </RandomTimeSection>

    ScannerTestConfig

  • 相关阅读:
    HDU 1082 Matrix Chain Multiplication
    HDU 1086 You can Solve a Geometry Problem too
    HDU 1099 Lottery
    jquery正则检测字符串表达式的合法性
    Like语句中的注入
    HDU 1372 Knight Moves
    HDU 1253 胜利大逃亡
    HDU 1242 Rescue
    我有新博客啦
    水平越权与垂直越权
  • 原文地址:https://www.cnblogs.com/GuliGugaLiz/p/7238051.html
Copyright © 2020-2023  润新知