iOS 做一个动态配置的大首页

对于电商App来说,首页一般都是一个多个内容聚合的页面。整体是列表但是里面内容特别杂。所以写代码的时候就要考虑逻辑要清晰,还要对类型的扩展要容易。
下面根据我18年写的一个代码说下这个事(主要是需求更新这部分代码没用了,去掉前留个纪念)。

先看下效果:
May-15-2023 16-58-25.gif

说一下这个需求,因为时间有点长记得不是很清楚了,就大致说下,头试图,cell,尾视图都有多种样式。根据相邻两个cell的样式不同间距也可能不同。

实现方案

整体结构:

页面整体就是一个collectionView,每一条后端数据都对应一个section,这样做的目的:1每一条数据都可能携带头部信息和尾部信息(不携带头部尾部时可以拼接在一起),2可能存在有二维数据的情况

1UI上拼接显示:
image.png

2:二维的数据:
image.png

代码实现

然后就是collectionView的代码的写法问题了。整体来看大的结构就是抽象工厂加策略
一,我写了一个管理类通过管理类现实cell的代理,把动态楼层(下面我们就这么叫吧)相关的逻辑拿了出来。只放了部分代码主要讲解思路。
调用部分

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    //动态模块section下的item
    KFZShopHomeCardsModel * cards = [_dataModel.cardsModel.localCardsList objectAtIndexCheck:section];
    return [KFZMainPageModuleCreater moduleItemCountWithModel:cards];
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    //动态模块创建
    KFZShopHomeCardsModel * cards = [_dataModel.cardsModel.localCardsList objectAtIndexCheck:indexPath.section];
    return [KFZMainPageModuleCreater createCellWithModel:cards CollectionView:collectionView IndexPath:indexPath];
}

工具类内部

@interface KFZDynamicModuleCellCeater : NSObject

//----cell
+(UICollectionViewCell *)createCellWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath;
+(NSInteger)moduleItemCountWithModel:(KFZShopHomeCardsModel *)moduleModel;
+(void)moduleItemClickWithModel:(KFZShopHomeCardsModel *)moduleModel IndexPath:(NSIndexPath *)indexPath;
+(CGSize)moduleItemSizeWithModel:(KFZShopHomeCardsModel *)moduleModel IndexPath:(NSIndexPath *)indexPath;

//----headerView
+(UICollectionReusableView *)createHeaderViewWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath;
+(CGSize)moduleIHeaderSizeWithModel:(KFZShopHomeCardsModel *)moduleModel;
+(UICollectionReusableView *)defautCollectionReusableViewCollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath;

//----footerView
+(UICollectionReusableView *)createFooterViewWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath;
+(CGSize)moduleIFooterSizeWithModel:(KFZShopHomeCardsModel *)moduleModel;
+(UICollectionReusableView *)defautCollectionFooterViewCollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath;
+(CGFloat)sameModuleSpaceHeight:(CardsType)cardType;


//----sectionLayout section各种间距
+(UIEdgeInsets)sectionInsetsWithModel:(KFZShopHomeCardsModel *)moduleModel;
+(CGFloat)sectionLineSpacingWithModel:(KFZShopHomeCardsModel *)moduleModel;
+(CGFloat)sectionItemSpacingWithModel:(KFZShopHomeCardsModel *)moduleModel;

//---regist
+(void)registAllClassIdentifirsCollectionView:(UICollectionView *)collectView;

@end
@implementation KFZDynamicModuleCellCeater
#pragma mark - 模块下 Cell

/**
 根据 模块的类型 创建cell
 
 @param moduleModel 模块数据
 @param collectionView 当前的collectionView
 @param indexPath 当前indexPath
 @return 创建的cell
 */
+(UICollectionViewCell *)createCellWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath{
    return [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType] createCellWithModel:moduleModel CollectionView:collectionView IndexPath:indexPath];
}

/**
 item的size
 
 @param moduleModel 数据
 @param indexPath 位置
 @return 计算出的size
 */
+(CGSize)moduleItemSizeWithModel:(KFZShopHomeCardsModel *)moduleModel IndexPath:(NSIndexPath *)indexPath{
    return [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType] moduleItemSizeWithModel:moduleModel IndexPath:indexPath];
}

/**
 item点击方法
 */
+(void)moduleItemClickWithModel:(KFZShopHomeCardsModel *)moduleModel IndexPath:(NSIndexPath *)indexPath{
    [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType] moduleItemClickWithModel:moduleModel IndexPath:indexPath];
}

/**
 每个模块item的个数
 */
+(NSInteger)moduleItemCountWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType]moduleItemCountWithModel:moduleModel];
}

#pragma mark - 模块中的各种间距

+(UIEdgeInsets)sectionInsetsWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType]sectionInsetsWithModel:moduleModel];
}

+(CGFloat)sectionLineSpacingWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType]sectionLineSpacingWithModel:moduleModel];
}

+(CGFloat)sectionItemSpacingWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return [[KFZDynamicModuleConfig factoryConfig:moduleModel.winddowShowType]sectionItemSpacingWithModel:moduleModel];
}


#pragma mark - 模块下 头试图

+(UICollectionReusableView *)createHeaderViewWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath{
    return [[KFZDynamicModuleConfig headerFactoryConfigWithHeaderStr:moduleModel.sectionHeaderStyleName]createHeaderViewWithModel:moduleModel CollectionView:collectionView IndexPath:indexPath];
}

+(CGSize)moduleIHeaderSizeWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return [[KFZDynamicModuleConfig headerFactoryConfigWithHeaderStr:moduleModel.sectionHeaderStyleName]moduleIHeaderSizeWithModel:moduleModel];
}

/**
 默认透明头试图
 */
+(UICollectionReusableView *)defautCollectionReusableViewCollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath{
    UICollectionReusableView * headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([UICollectionReusableView class]) forIndexPath:indexPath];
    headerView.backgroundColor = [UIColor clearColor];
    return headerView;
}

#pragma mark - 模块下 尾视图 *** 模块之前的间隔 由这里控制

+(UICollectionReusableView *)createFooterViewWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath{
    if (moduleModel.isHaveFooter) {
        KFZShopHomeAreaFooterView * footer = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:NSStringFromClass([KFZShopHomeAreaFooterView class]) forIndexPath:indexPath];
        if ([moduleModel.showMoreType isEqualToString:@"1"]) {
            footer.footerType = KFZShopHomeFooterType_LookMore;
        }else{
            footer.footerType = KFZShopHomeFooterType_FreshList;
        }
        footer.sectionIndex = indexPath.section;
        footer.sectionModel = moduleModel;
        
        __weak typeof(collectionView)wkCollection = collectionView;
        footer.selfBeClick = ^(NSInteger section) {
            [wkCollection reloadSections:[NSIndexSet indexSetWithIndex:section]];
        };
        return footer;
    }
    return [self defautCollectionFooterViewCollectionView:collectionView IndexPath:indexPath];
}

+(CGSize)moduleIFooterSizeWithModel:(KFZShopHomeCardsModel *)moduleModel{
    if (moduleModel.isHaveFooter) {
        KFZShopHomeCardsModel * nextModel  = moduleModel.nextLink;
        if (nil != nextModel && (nextModel.HaveHeaderTitle || nextModel.HaveHeaderScran)) {
            return CGSizeMake(DEF_SCREEN_WIDTH, 70 + 10);
        }
        return CGSizeMake(DEF_SCREEN_WIDTH, 70);
    }else{
        
        KFZShopHomeCardsModel * nextModel = moduleModel.nextLink;
        
        //要判断两个模块之间的距离 所以取值上保证不是最后一个模块
        if (nil != nextModel) {
            
            //如果下一个模块有头试图的话 算是两个模块了 当前模块向下的间隔应该是47
            if (nextModel.HaveHeaderTitle || nextModel.HaveHeaderScran || nextModel.HaveHeaderLabels) {
                return CGSizeMake(DEF_SCREEN_WIDTH, 47);
            }
            
            //如果没有头试图的时候
            
            //判断两个模块是否是一种类型
            if (moduleModel.winddowShowType == nextModel.winddowShowType) {
                return CGSizeMake(DEF_SCREEN_WIDTH, [self sameModuleSpaceHeight:moduleModel.winddowShowType]);
            }
            
            //两种模块不是一种类型
            
            //两种模块不是一种类型   但是两种模块分别是两格(twoCard)和三格(threeCard)
            if ((moduleModel.winddowShowType == CardsType_twoCard || moduleModel.winddowShowType == CardsType_threeCard) && (nextModel.winddowShowType == CardsType_twoCard || nextModel.winddowShowType == CardsType_threeCard)) {
                return CGSizeMake(DEF_SCREEN_WIDTH, 3);
            }
            //两种模块不是一种类型   但是两种模块分别是一个高(OneCard_height)和一格矮(OneCard)
            else if ((moduleModel.winddowShowType == CardsType_OneCard_height || moduleModel.winddowShowType == CardsType_OneCard) && (nextModel.winddowShowType == CardsType_OneCard_height || nextModel.winddowShowType == CardsType_OneCard)) {
                return CGSizeMake(DEF_SCREEN_WIDTH, 3);
            }
            else{
                return CGSizeMake(DEF_SCREEN_WIDTH, 47);
            }
        }
        
        //最后一个模块的逻辑
        
        //如果是猜你喜欢 下面会有一个查看更多的尾试图所以要把间距改为0
        if (moduleModel.winddowShowType == CardsType_GuessYouLike) {
            return CGSizeMake(DEF_SCREEN_WIDTH, 0);;
        }
        
    }
    return CGSizeMake(DEF_SCREEN_WIDTH, 0);
}

/**
 默认透明尾试图
 */
+(UICollectionReusableView *)defautCollectionFooterViewCollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath{
    UICollectionReusableView * headerView = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:NSStringFromClass([UICollectionReusableView class]) forIndexPath:indexPath];
    headerView.backgroundColor = [UIColor clearColor];
    return headerView;
}

/**
 上下两个是同类型时的间距
 */
+(CGFloat)sameModuleSpaceHeight:(CardsType)cardType{
    
    switch (cardType) {
        case CardsType_OneCard_height:{
            return 3;
        }
        case CardsType_OneCard:{
            return 3;
        }
        case CardsType_twoCard:{
            return 3;
        }
        case CardsType_threeCard:{
            return 3;
        }
        case CardsType_preciousBook:{
            return 25;
        }
        case CardsType_goodBook:{
            return 25;
        }
        case CardsType_goodShop:{
            return 15;
        }
        case CardsType_GuessYouLike:{
            return 10;
        }
        case CardsType_BookList:{
            return 47;
        }
        default:
            return 47;
    }
}



#pragma mark - 注册方法

/**
 像当前的collectionView注册cell和头尾试图 --- (一点不好 所有的都要注册现有的全部类型)
 
 @param collectView 当前的collectionView
 */
+(void)registAllClassIdentifirsCollectionView:(UICollectionView *)collectView{
    [collectView registerClass:[KFZShopThreeCardCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZShopThreeCardCell class])];
    [collectView registerClass:[KFZShopTwoCardCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZShopTwoCardCell class])];
    [collectView registerClass:[KFZOneHeightCardCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZOneHeightCardCell class])];
    [collectView registerClass:[KFZOneLowCardCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZOneLowCardCell class])];
    [collectView registerClass:[KFZImageTitlePrise_GoodsCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZImageTitlePrise_GoodsCell class])];
    [collectView registerClass:[KFZImageTitleAuthorPrise_GoodsCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZImageTitleAuthorPrise_GoodsCell class])];
    [collectView registerClass:[KFZShopHomeAreaShopCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZShopHomeAreaShopCell class])];
    [collectView registerClass:[KFZGuessYourLikeCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZGuessYourLikeCell class])];
    [collectView registerClass:[KFZChannelBoolListCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZChannelBoolListCell class])];
    [collectView registerClass:[KFZISBNHorizontalScrollCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZISBNHorizontalScrollCell class])];
    [collectView registerClass:[KFZISBNTableStyleCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZISBNTableStyleCell class])];
    [collectView registerClass:[KFZGoodsHorizontalScrollCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZGoodsHorizontalScrollCell class])];
    [collectView registerClass:[KFZAuctionHorizontalScrollCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZAuctionHorizontalScrollCell class])];
    [collectView registerClass:[KFZGoodsTableStyleCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZGoodsTableStyleCell class])];
    [collectView registerClass:[KFZAuctionSpecialHorizontalScrollCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZAuctionSpecialHorizontalScrollCell class])];
    [collectView registerClass:[KFZAuctionAdvertisementCell class] forCellWithReuseIdentifier:NSStringFromClass([KFZAuctionAdvertisementCell class])];
    
    [collectView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:NSStringFromClass([UICollectionReusableView class])];
    [collectView registerClass:[KFZShopHomeAreaFooterView class] forSupplementaryViewOfKind:UICollectionElementKindSectionFooter withReuseIdentifier:NSStringFromClass([KFZShopHomeAreaFooterView class])];
    [collectView registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([UICollectionReusableView class])];
    [collectView registerClass:[KFZOnlyTitleHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([KFZOnlyTitleHeaderView class])];
    [collectView registerClass:[KFZOnlyScreenHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([KFZOnlyScreenHeaderView class])];
    [collectView registerClass:[KFZTitleAndScreenHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([KFZTitleAndScreenHeaderView class])];
    [collectView registerClass:[KFZOnlyLabelsHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([KFZOnlyLabelsHeaderView class])];
    [collectView registerClass:[KFZTitleAndLabelsHeaderView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:NSStringFromClass([KFZTitleAndLabelsHeaderView class])];
    
}
@end

二,可以看到上面代码KFZDynamicModuleConfig这个算是一个策略选择不过我内部是用映射是生成对应的工厂的。(正常写的话肯定是先写出工厂然后才有这个,不过看代码正好是可以按照这个关联性去看)

@interface KFZDynamicModuleConfig : NSObject

/**
 根据 模块类型枚举的值 来取工厂类

 @return 返回对应的工厂类
 */
+(KFZDynamicModuleCellFactory *)factoryConfig:(CardsType)type;

/**
 根据 模块类型枚举的值 来取工厂类
 
 @return 返回对应的工厂类
 */
+(KFZDynamicModuleHeaderFactory *)headerFactoryConfig:(CardsHeadType)type;

/**
 根据 头试图描述字符串 来取工厂类
 
 @return 返回对应的工厂类
 */
+(KFZDynamicModuleHeaderFactory *)headerFactoryConfigWithHeaderStr:(NSString *)headerStr;


/** 通知-通知频道页面刷新(ps:主要做换一换功能只是在当前页面的一个通知,页面不显示就移除) */
FOUNDATION_EXPORT NSString *const ChannelController_FreshSection;

@end
···
···
#import "KFZDynamicModuleConfig.h"

@implementation KFZDynamicModuleConfig
+(KFZDynamicModuleCellFactory *)factoryConfig:(CardsType)type{
    NSArray * list = @[@"KFZOneCardHeightFactory"
                       ,@"KFZOneCardLowFactory"
                       ,@"KFZTwoCardFactory"
                       ,@"KFZThreeCardFactory"
                       ,@"KFZVerticalBookFactory"
                       ,@"KFZVerticalISBNFactory"
                       ,@"KFZVerticalShopFactory"
                       ,@"KFZGuessYouLikeGoodsFactory"
                       ,@"KFZHorizontalBookListFactory"
                       ,@"KFZISBNHorizontalScrollFactory"
                       ,@"KFZISBNTableStyleFactory"
                       ,@"KFZGoodsHorizontalScrollFactory"
                       ,@"KFZAuctionHorizontalScrollFactory"
                       ,@"KFZGoodsTableStyleFactory"
                       ,@"KFZAuctionSpecialHorizontalScrollFactory"
                       ,@"KFZAuctionAdvertisementFactory"];
    NSString * className = list[type];
    Class class = NSClassFromString(className);
    KFZDynamicModuleCellFactory * factory = [class new];
    return factory;
}

+(KFZDynamicModuleHeaderFactory *)headerFactoryConfig:(CardsHeadType)type{
    NSArray * list = @[@"KFZPureColor_WhiteHeaderFactory"
                       ,@"KFZOnlyTitleHeaderFactory"
                       ,@"KFZOnlyScreenHeaderFactory"
                       ,@"KFZTitleAndScreenHeaderFactory"
                       ,@"KFZOnlyLabelsHeaderFactory"
                       ,@"KFZTitleAndLabelsHeaderFactory"];
    NSString * className = list[type];
    Class class = NSClassFromString(className);
    KFZDynamicModuleHeaderFactory * factory = [class new];
    return factory;
}

+(KFZDynamicModuleHeaderFactory *)headerFactoryConfigWithHeaderStr:(NSString *)headerStr{
    NSDictionary * list = @{@"haveTitlehaveScranhaveLabels":@"KFZTitleAndScreenHeaderFactory",//=
                            @"haveTitlehaveScrannoLabels":@"KFZTitleAndScreenHeaderFactory",
                            @"haveTitlenoScranhaveLabels":@"KFZTitleAndLabelsHeaderFactory",
                            @"haveTitlenoScrannoLabels":@"KFZOnlyTitleHeaderFactory",
                            @"noTitlehaveScranhaveLabels":@"KFZOnlyScreenHeaderFactory",//=
                            @"noTitlehaveScrannoLabels":@"KFZOnlyScreenHeaderFactory",
                            @"noTitlenoScranhaveLabels":@"KFZOnlyLabelsHeaderFactory",
                            @"noTitlenoScrannoLabels":@"KFZPureColor_WhiteHeaderFactory",
                            
                            };
    NSString * className = list[headerStr];
    Class class = NSClassFromString(className);
    KFZDynamicModuleHeaderFactory * factory = [class new];
    return factory;
}

NSString *const ChannelController_FreshSection = @"com.Kongfz.ChannelController_FreshSection";//通知-通知频道页面刷新

@end

这里面的一些枚举,其实就是后端标识的数据类型,我这面转成枚举了。
第三,就是多个工厂的实现了。在KFZDynamicModuleConfig可以看见我返回工厂时的类是KFZDynamicModuleCellFactory这里我是使用基类的方式代替了协议了。
这里我就举个简单的例子吧,就上面一横行两个图片的那种。

@interface KFZTwoCardFactory : KFZDynamicModuleCellFactory
@end
@implementation KFZTwoCardFactory
//----cell
-(UICollectionViewCell *)createCellWithModel:(KFZShopHomeCardsModel *)moduleModel CollectionView:(UICollectionView *)collectionView IndexPath:(NSIndexPath *)indexPath{
    KFZShopTwoCardCell * cell = [collectionView dequeueReusableCellWithReuseIdentifier:NSStringFromClass([KFZShopTwoCardCell class]) forIndexPath:indexPath];
    cell.model = moduleModel;
    return cell;
}
-(NSInteger)moduleItemCountWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return 1;
}
-(void)moduleItemClickWithModel:(KFZShopHomeCardsModel *)moduleModel IndexPath:(NSIndexPath *)indexPath{
    //cellb内部做跳转了
}
-(CGSize)moduleItemSizeWithModel:(KFZShopHomeCardsModel *)moduleModel IndexPath:(NSIndexPath *)indexPath{
    return CGSizeMake(DEF_SCREEN_WIDTH - 30, 66*SHOPHOMEAREA_WIDTH_RATIO);
}

//----sectionLayout section各种间距
-(UIEdgeInsets)sectionInsetsWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return UIEdgeInsetsMake(0, 15, 0, 15);
}
-(CGFloat)sectionLineSpacingWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return 25;
}
-(CGFloat)sectionItemSpacingWithModel:(KFZShopHomeCardsModel *)moduleModel{
    return 15*SHOPHOMEAREA_WIDTH_RATIO;
}
@end

整体结构就这样说完了,其实也不是很复杂是不是。最后附加一个我这里类型的截图:


image.png

打完收工!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容