Skip to main content

typecho隐藏首页某一分类下的文章

· 2 min read

场景需求

有些博主想要做一些特殊的需求,比如本站,摄影模块就是特殊处理,摄影分类并不在首页展示。

解决方案

  • 步骤一:隐藏侧边栏的分类
    找到主题下的sidebar.php文件,找到代码$this->widget('Widget_Metas_Category_List')修改为$this->widget('Widget_Metas_Category_List@options','ignore=13')其中13为想要隐藏分类的mid
  • 步骤二:隐藏首页文章列表的摄影分类文章
    采用插件CateFilterhttps://github.com/typecho-fans/plugins/tree/master/CateFilter隐藏,启动插件后,设置首页想要隐藏的分类的mid即可
  • 步骤三:隐藏文章详情中上一篇下一篇中的摄影分类文章
    找到/var/Widget/Archive.php文件,修改thePrevtheNext方法

thePrev方法$content部分代码替换如下,13为摄影分类mid

 $content = $this->db->fetchRow($this->select()->from('table.contents')
->join('table.relationships', 'table.relationships.cid = table.contents.cid')
->where('table.relationships.mid != ?', '13')
->where('table.contents.created < ?',$this->created)
->where('table.contents.status = ?', 'publish')
->where('table.contents.type = ?', $this->type)
->where('table.contents.password IS NULL')
->order('table.contents.created', Typecho_Db::SORT_DESC)
->limit(1));

theNext方法$content部分代码替换如下,13为摄影分类mid

 $content = $this->db->fetchRow($this->select()
->join('table.relationships', 'table.relationships.cid = table.contents.cid')
->where('table.relationships.mid != ?', '13')
->where('table.contents.created > ? AND table.contents.created < ?',$this->created, $this->options->time)
->where('table.contents.status = ?', 'publish')
->where('table.contents.type = ?', $this->type)
->where('table.contents.password IS NULL')
->order('table.contents.created', Typecho_Db::SORT_ASC)
->limit(1));

puppeteer安装太慢,更换为国内镜像

· One min read

需求场景

由于墙的原因,采用npm i puppeteer -S安装puppeteer非常的慢。博主就深受折磨,搜遍了全网,在没有梯子的情况下采用淘宝镜像安装是最快的方式,快的飞起的那种。

解决方法

PUPPETEER_DOWNLOAD_HOST=https://npm.taobao.org/mirrors npm i puppeteer -S 

使用此方法安装快速而又无副作用,可以说是最优解了。

nestjs 上传文件(本地或七牛云)

· 3 min read

上传文件至本地

//file.module.ts
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MulterModule } from '@nestjs/platform-express';
import { FileController } from './file.controller';
import { FileService } from './file.service';
import { diskStorage } from 'multer';
import * as dayjs from 'dayjs';
import { nanoid } from 'nanoid';
import { SequelizeModule } from '@nestjs/sequelize';
import { File } from './file.model';
import * as mkdirp from 'mkdirp';

@Module({
imports: [
SequelizeModule.forFeature([File]),
MulterModule.registerAsync({
useFactory: async (configService: ConfigService) => ({
storage: diskStorage({
destination: (req, file, cb) => {
const dest = `${configService.get('UPLOAD_DEST')}/${dayjs().format(
'YYYY-MM-DD',
)}`;
mkdirp(dest, function () {
return cb(null, dest);
});
},
filename: (req, file, cb) => {
const uniqueSuffix = Date.now() + '-' + nanoid();
cb(null, uniqueSuffix + '-' + file.originalname);
},
}),
}),
inject: [ConfigService],
}),
],
controllers: [FileController],
providers: [FileService],
})
export class FileModule {}
//file.controller.ts
import {
Controller,
Post,
UploadedFile,
UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { FileService } from './file.service';

@Controller('file')
export class FileController {
constructor(private fileService: FileService) {}

@Post('upload')
@UseInterceptors(FileInterceptor('file'))
async uploadFile(@UploadedFile() file: Express.Multer.File) {
const data = await this.fileService.upload(file);
return {
errcode: 0,
data,
};
}
}

上传至七牛云

//file.module.ts
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { MulterModule } from '@nestjs/platform-express';
import { FileController } from './file.controller';
import { FileService } from './file.service';
import { SequelizeModule } from '@nestjs/sequelize';
import { File } from './file.model';

@Module({
imports: [
SequelizeModule.forFeature([File]),
MulterModule.registerAsync({
useFactory: async (configService: ConfigService) => ({
dest: configService.get('UPLOAD_DEST'),
}),
inject: [ConfigService],
}),
],
controllers: [FileController],
providers: [FileService],
})
export class FileModule {}
//file.service.ts
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import { InjectModel } from '@nestjs/sequelize';
import { File } from './file.model';
import * as qiniu from 'qiniu';
import { ConfigService } from '@nestjs/config';
import { nanoid } from 'nanoid';
import * as fs from 'fs-extra';
import * as path from 'path';

@Injectable()
export class FileService {
constructor(
private configService: ConfigService,

@InjectModel(File)
private fileModel: typeof File,
) {}

async upload(file: Express.Multer.File) {
const url = await this.uploadToQiniu(file);
const fileInfo = {
originalname: file.originalname,
size: file.size,
mimetype: file.mimetype,
url: url,
};
await this.fileModel.create(fileInfo);
await fs.remove(file.path);
return fileInfo;
}

async uploadToQiniu(file: Express.Multer.File): Promise<string> {
const mac = new qiniu.auth.digest.Mac(
this.configService.get('QINIU_ACCESSKEY_ID'),
this.configService.get('QINIU_ACCESSKEY_SECRET'),
);
const putPolicy = new qiniu.rs.PutPolicy({
scope: this.configService.get('QINIU_BUCKET'),
});
const uploadToken = putPolicy.uploadToken(mac);

const formUploader = new qiniu.form_up.FormUploader(
new qiniu.conf.Config({
zone: qiniu.zone.Zone_z0,
}),
);

const filename =
Date.now() + '-' + nanoid() + path.extname(file.originalname);

const qiniuHost = this.configService.get('QINIU_HOST');
return new Promise((resolve) => {
formUploader.put(
uploadToken,
filename,
file.buffer,
new qiniu.form_up.PutExtra(),
function (respErr, respBody, respInfo) {
if (respErr) {
console.error(respErr);
throw new InternalServerErrorException(respErr.message);
}
if (respInfo.statusCode == 200) {
resolve(new URL(respBody.key, qiniuHost).href);
} else {
console.error(respInfo.statusCode, respBody);
throw new InternalServerErrorException(respInfo);
}
},
);
});
}
}

nestjs 自定义ValidationPipe

· 2 min read

需求场景

nestjs内置的ValidationPipe验证管道的返回响应如下

{
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"target": {},
"property": "email",
"children": [],
"constraints": {
"isEmail": "email must be an email"
}
}
]
}

而实际生产项目中,参数验证往往返回200状态码,我们可能需要形如

{
success:0,
message:'',
data:[]
}

的响应返回。这个时候就需要自定义验证管道。

修改main.ts

通过查看validationPipe源码可以发现(nestjs v7文档中已有,v6文档中没有,似乎也不支持),ValidationPipe有个参数exceptionFactory就是用来自定义返回错误的。修改的具体源码如下

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe, HttpStatus, HttpException } from '@nestjs/common';

async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (validationErrors = []) => {
throw new HttpException(
{
success: 0,
message: Object.values(validationErrors[0].constraints)[0],
},
HttpStatus.OK,
);
},
}),
);
await app.listen(6000);
}
bootstrap();

文本溢出加省略号

· One min read

单行文本溢出

     p{
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

多行文本溢出

    p{
width: 100%;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp:3;
-webkit-box-orient:vertical;
}

angular8 自定义webpack,postcss移动端适配

· 3 min read

移动端适配往往使用vw,rem等单位,一般配合postcss使用
angular8官方文档中并没有提及自定义webpack的修改,不过好在有个第三方模块可以帮助我们自定义webpack
第一步安装 npm i @angular-builders/custom-webpack --save-dev
第二步修改angular.json

 "architect": {
...
"build": {
"builder": "@angular-builders/custom-webpack:browser",
"options": {
"customWebpackConfig": {
"path": "./webpack.config.js"
},
...
}
},
"serve": {
"builder": "@angular-builders/custom-webpack:dev-server",
"options": {
"browserTarget": "my-project:build"
}
}

第三步在根目录下面新建webpack.config.js

const path = require("path");
const SpritesmithPlugin = require("webpack-spritesmith");

// postcss插件
const postcssLoader = {
loader: "postcss-loader",
options: {
ident: "postcss",
syntax: "postcss-less",
plugins: () => [
require("postcss-px-to-viewport")({
viewportWidth: 375, // 视窗的宽度,对应的是我们设计稿的宽度,一般是750
viewportHeight: 667, // 视窗的高度,根据750设备的宽度来指定,一般指定1334,也可以不配置
unitPrecision: 3, // 指定`px`转换为视窗单位值的小数位数
viewportUnit: "vw", //指定需要转换成的视窗单位,建议使用vw
selectorBlackList: [".ignore"], // 指定不转换为视窗单位的类,可以自定义,可以无限添加,建议定义一至两个通用的类名
minPixelValue: 1, // 小于或等于`1px`不转换为视窗单位,你也可以设置为你想要的值
mediaQuery: false
}),
require("autoprefixer")({
overrideBrowserslist: [
"Android 4.1",
"iOS 7.1",
"Chrome > 31",
"ff > 31",
"ie >= 8"
],
grid: true
})
]
}
};

// 雪碧图插件
const spritePlugin = new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, "src/assets/images"),
glob: "*.png"
},
target: {
image: path.resolve(__dirname, "src/assets/sprite.png"),
css: [
path.resolve(__dirname, "src/assets/sprite.css"),
[
path.resolve(__dirname, "src/assets/sprite.json"),
{ format: "json_texture" }
]
]
},
apiOptions: {
cssImageRef: "assets/sprite.png"
}
});

module.exports = (config, options) => {
// config就是系统的webpack配置
// 第一步过滤掉系统的css和less处理

config.module.rules = config.module.rules.filter(
rule => rule.test.toString() !== "/\\.less$/"
);

// 配置自定义的less处理
config.module.rules.push({
test: /\.(less)$/,
exclude: [
path.resolve(__dirname, "src/styles.less")
],
use: ["raw-loader", postcssLoader, "less-loader"]
});

config.module.rules.push({
test: /\.(less)$/,
include: [
path.resolve(__dirname, "src/styles.less")
],
use: ["style-loader", postcssLoader, "less-loader"]
});

config.plugins.push(spritePlugin);

return config;
};

在webpack的配置中,建议先console一下原先的配置,然后根据自己的需求去修改对应的loader,这样不容易出错。