提交 4bc59651 编写于 作者: B baiy 提交者: ninecents

添加文本处理工具 #58 #55

上级 0e4c3d4e
# 程序开发常用工具
使用过程中的任何问题或者需要新的工具欢迎提交`Issue`,新工具如果可以提供实现代码就完美了O(∩_∩)O
## chrome 安装
- 方法1: 在 [Chrome 应用商店](https://chrome.google.com/webstore/detail/ipfcebkfhpkjeikaammlkcnalknjahmh) 安装
- 方法2: [下载 .crx 安装包](https://github.com/baiy/Ctool/releases/latest)手动安装 [猛戳这里查看手动安装教程](http://www.cnplugins.com/tool/outline-install-crx-file.html)
- 方法3: [百度网盘下载](https://pan.baidu.com/s/1mhWbqWC) 安装方法和方法2一致
- 方法1: 在 [Chrome 应用商店](https://chrome.google.com/webstore/detail/ipfcebkfhpkjeikaammlkcnalknjahmh) 安装
- 方法2: [下载 .crx 安装包](https://github.com/baiy/Ctool/releases/latest)手动安装 [猛戳这里查看手动安装教程](http://www.cnplugins.com/tool/outline-install-crx-file.html)
- 方法3: [百度网盘下载](https://pan.baidu.com/s/1mhWbqWC) 安装方法和方法2一致
> 方法2 / 方法3 不定期维护 仅供网络环境特别恶劣的同学使用
### 本地打包/调试
```
# 打包
npm run build -adapter=chrome
......@@ -26,6 +30,7 @@ npm run serve -adapter=chrome
> 插件中心搜索`ctool`
### 本地打包/调试
```
# 打包
npm run build -adapter=utools
......@@ -36,6 +41,7 @@ npm run serve -adapter=utools
```
## 功能列表
|功能|说明|离线使用|
|---|---|---|
|哈希|`md5`, `sha1`, `sha256`, `sha512`,`sm3`|√|
......@@ -64,26 +70,28 @@ npm run serve -adapter=utools
|变量名格式转换|`Var Name`, `var-name`, `VAR_NAME`, `VarName`, `varName`, `var_name`, `var name`|√|
|jwt解码|`header`, `payload`|√|
|Hex/String转换|`hex to string`, `string to hex`, `十六进制转字符串`, `字符串转十六进制`|√|
|文本处理|'大小写转换', '中英文标点转换', '简繁转换', '替换', '字符统计', '行去重', '添加行号', '行排序', '过滤行首尾不可见字符','过滤空行'|√|
## 第三方开源库
项目诞生离不开这些优秀的开源程序
- [ajax-request](https://www.npmjs.com/package/ajax-request)
- [ajax-request](https://www.npmjs.com/package/ajax-request)
- [code-formatter](https://www.npmjs.com/package/code-formatter)
- [crypto-js](https://www.npmjs.com/package/crypto-js)
- [ipinyinjs](https://www.npmjs.com/package/ipinyinjs)
- [is-url](https://www.npmjs.com/package/is-url)
- [iview](https://www.npmjs.com/package/iview)
- [js-base64](https://www.npmjs.com/package/js-base64)
- [lscache](https://www.npmjs.com/package/lscache)
- [php-array-reader](https://www.npmjs.com/package/php-array-reader)
- [phparr](https://www.npmjs.com/package/phparr)
- [qrcode](https://www.npmjs.com/package/qrcode)
- [qrcode-parser](https://www.npmjs.com/package/qrcode-parser)
- [ipinyinjs](https://www.npmjs.com/package/ipinyinjs)
- [is-url](https://www.npmjs.com/package/is-url)
- [iview](https://www.npmjs.com/package/iview)
- [js-base64](https://www.npmjs.com/package/js-base64)
- [lscache](https://www.npmjs.com/package/lscache)
- [php-array-reader](https://www.npmjs.com/package/php-array-reader)
- [phparr](https://www.npmjs.com/package/phparr)
- [qrcode](https://www.npmjs.com/package/qrcode)
- [qrcode-parser](https://www.npmjs.com/package/qrcode-parser)
- [radix.js](https://www.npmjs.com/package/radix.js)
- [serialize-php](https://www.npmjs.com/package/serialize-php)
- [diff](https://www.npmjs.com/package/diff)
- [vue](https://www.npmjs.com/package/vue)
- [serialize-php](https://www.npmjs.com/package/serialize-php)
- [diff](https://www.npmjs.com/package/diff)
- [vue](https://www.npmjs.com/package/vue)
- [vue-router](https://www.npmjs.com/package/vue-router)
- [taobao](http://ip.taobao.com/)
- [layui](https://github.com/sentsin/layui/)
......@@ -99,8 +107,10 @@ npm run serve -adapter=utools
- [sm-crypto](https://github.com/JuneAndGreen/sm-crypto)
- [camelcaseplugin](https://github.com/netnexus/camelcaseplugin)
- [jwt-decode](https://www.npmjs.com/package/jwt-decode)
- [jian_fan](https://www.npmjs.com/package/jian_fan)
> 当然项目中还使用很多不知道姓名的大神的代码, 在这里就不一一感谢
## 先睹为快
> 当然项目中还使用很多不知道姓名的大神的代码, 在这里就不一一感谢
## 先睹为快
![](https://cdn.jsdelivr.net/gh/baiy/Ctool/dome.jpg)
![](https://cdn.jsdelivr.net/gh/baiy/Ctool/dome.jpg)
{
"name": "c-tool",
"version": "1.6.2",
"version": "1.6.5",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
......@@ -6674,6 +6674,11 @@
"integrity": "sha1-FC0RHzpuPa6PSpr9d9RYVbWpzOM=",
"dev": true
},
"jian_fan": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/jian_fan/-/jian_fan-1.0.3.tgz",
"integrity": "sha512-b7Dr2hckX8zViAcEMBMQ/SPtqmfmqQ9Za6W9KD05rcZpb52E2PzxPUwTn0H2TMDv4gaqfa5tUrGNLci9dcnLxw=="
},
"js-base64": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz",
......
{
"name": "c-tool",
"version": "1.6.5",
"version": "1.6.6",
"private": true,
"scripts": {
"serve": "vue-cli-service serve --port 8081",
......@@ -19,6 +19,7 @@
"http-build-query": "^0.7.0",
"ipinyinjs": "^1.0.0",
"is-url": "^1.2.4",
"jian_fan": "^1.0.3",
"js-base64": "^2.6.4",
"js-yaml": "^3.14.1",
"json-to-properties": "^1.1.3",
......
// 工具缓存数据过期时间(秒)
const TOOL_DATA_EXPIRY = 3600 * 24
// 徽章过期时间(天)
const BADGE_EXPIRY = 5
// 分类徽章
const BADGE_CATEGORY = ['other']
// 工具徽章
const BADGE_TOOL = ['text','hexString']
// 默认常用工具
const DEFAULT_COMMON_TOOL = [
'hash', 'encrypt', 'json', 'base64', 'url', 'timestamp',
'qrCode', 'pinyin', 'ip', 'code', 'unicode',
'text', 'randomString', 'diffs',
]
const category = [
{'name': 'common', 'title': '常用工具'},
{'name': 'encryption', 'title': '解密'},
{'name': 'conversion', 'title': '编码转换'},
{'name': 'common', 'title': '常用'},
{'name': 'encryption', 'title': '加解密'},
{'name': 'conversion', 'title': '转换'},
{'name': 'serialize', 'title': '序列化'},
{'name': 'other', 'title': '其他工具'},
{'name': 'check', 'title': '校验'},
{'name': 'generate', 'title': '生成'},
{'name': 'other', 'title': '其他'},
]
const tool = [
......@@ -27,20 +44,21 @@ const tool = [
{'name': 'code', 'title': '代码格式化', 'cat': ['other']},
{'name': 'unicode', 'title': 'Unicode', 'cat': ['conversion']},
{'name': 'decimalConvert', 'title': '进制转换', 'cat': ['conversion']},
{'name': 'regex', 'title': '正则表达式', 'cat': ['other']},
{'name': 'randomString', 'title': '随机字符生成', 'cat': ['other']},
{'name': 'regex', 'title': '正则表达式', 'cat': ['check']},
{'name': 'randomString', 'title': '随机字符生成', 'cat': ['generate']},
{'name': 'serializeConversion', 'title': '序列化转换', 'cat': ['conversion', 'serialize']},
{'name': 'diffs', 'title': '文本差异化对比', 'cat': ['other']},
{'name': 'crontab', 'title': 'crontab校验', 'cat': ['other']},
{'name': 'diffs', 'title': '文本差异化对比', 'cat': ['check']},
{'name': 'crontab', 'title': 'crontab校验', 'cat': ['check']},
{'name': 'websocket', 'title': 'websocket调试', 'cat': ['other']},
{'name': 'unit', 'title': '单位换算', 'cat': ['other']},
{'name': 'time', 'title': '时间计算器', 'cat': ['other']},
{'name': 'uuid', 'title': 'UUID生成', 'cat': ['other']},
{'name': 'uuid', 'title': 'UUID生成', 'cat': ['generate']},
{'name': 'jsonToObject', 'title': 'JSON转实体类', 'cat': ['conversion', 'serialize']},
{'name': 'ascii', 'title': 'ascii转换', 'cat': ['conversion']},
{'name': 'ascii', 'title': 'ASCII转换', 'cat': ['conversion']},
{'name': 'variableConversion', 'title': '变量名转换', 'cat': ['conversion']},
{'name': 'jwt', 'title': 'jwt解码', 'cat': ['conversion']},
{'name': 'jwt', 'title': 'JWT解码', 'cat': ['conversion']},
{'name': 'hexString', 'title': 'Hex/String转换', 'cat': ['conversion']},
{'name': 'text', 'title': '文本处理', 'cat': ['other']},
]
// 工具类功能配置
......@@ -57,6 +75,7 @@ const utools = {
encrypt: ['AES', 'DES', 'RC4', 'Rabbit', 'TripleDes', 'sm2'],
jwt: ['jwtDecode'],
hexString: ['hex to string', 'string to hex', '十六进制转字符串', '字符串转十六机制'],
text: ['文本处理', '大小写转换', '中英文标点转换', '简繁转换', '字符替换', '字符统计', '行去重', '添加行号', '行排序', '过滤行首尾不可见字符', '过滤空行'],
},
cmds: {
timestamp: [
......@@ -104,5 +123,10 @@ module.exports = {
category,
tool,
feature,
utools
utools,
toolDataExpiry: TOOL_DATA_EXPIRY,
badgeExpiry: BADGE_EXPIRY,
badgeCategory: BADGE_CATEGORY,
badgeTool: BADGE_TOOL,
defaultCommonTool: DEFAULT_COMMON_TOOL
}
\ No newline at end of file
......@@ -109,6 +109,10 @@ const routes = [
{
path: '/tool/hexString',
component: r => require(['./views/tool/hexString.vue'], r)
},
{
path: '/tool/text',
component: r => require(['./views/tool/text.vue'], r)
}
]
......
import {env, inArray} from '../helper'
import cache from './cache'
const toolConfig = require('../config')
// 工具缓存数据过期时间(秒)
export const TOOL_DATA_EXPIRY = 3600 * 24
export const TOOL_DATA_EXPIRY = toolConfig.toolDataExpiry
// 徽章过期时间(天)
export const BADGE_EXPIRY = 10
export const BADGE_EXPIRY = toolConfig.badgeExpiry
// 分类徽章
export const BADGE_CATEGORY = []
export const BADGE_CATEGORY = toolConfig.badgeCategory
// 工具徽章
export const BADGE_TOOL = []
export const BADGE_TOOL = toolConfig.badgeTool
// 默认常用工具
export const DEFAULT_COMMON_TOOL = [
'hash', 'encrypt', 'json', 'base64', 'url', 'timestamp',
'qrCode', 'pinyin', 'ip', 'code', 'unicode',
'decimalConvert', 'randomString', 'diffs',
]
const toolConfig = require('../config')
export const DEFAULT_COMMON_TOOL = toolConfig.defaultCommonTool
const category = toolConfig.category
......
import language from 'jian_fan'
import _ from 'lodash'
const regExpQuote = function (str) {
return str.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
};
// GBK字符集实际长度计算
function getGbkStrLength(str) {
let realLength = 0;
let len = str.length;
let charCode = -1;
for (let i = 0; i < len; i++) {
charCode = str.charCodeAt(i);
if (charCode >= 0 && charCode <= 128) {
realLength += 1;
} else {
realLength += 2;
}
}
return realLength;
}
class TextHandle {
constructor(text) {
this.text = text;
}
// 大写
upper() {
return this.text.toUpperCase();
}
// 小写
lower() {
return this.text.toLowerCase();
}
// 行首大写
upperLineStart() {
return this.text.split(/\r?\n/).map((str) => {
return str[0].toUpperCase() + str.substr(1)
}).join("\n");
}
// 行首小写
lowerLineStart() {
return this.text.split(/\r?\n/).map((str) => {
return str[0].toLowerCase() + str.substr(1)
}).join("\n");
}
// 词首大写
upperStart() {
return this.text.replace(/\b\w/g, function (str) {
return str.toUpperCase();
});
}
// 词首小写
lowerStart() {
return this.text.replace(/\b\w/g, function (str) {
return str.toLowerCase();
});
}
// 简繁转换
zhTran(type = "simplified") {
if (type === "simplified") {
return language.simplified(this.text)
}
return language.traditional(this.text)
}
// 替换
replace(search = [], replace = []) {
let text = this.text;
for (let i in search) {
if (search[i]){
text = text.replace(new RegExp(regExpQuote(search[i]), 'g'), (i in replace ? replace[i] : ""));
}
}
return text;
}
// 移除重复行
lineRemoveRepeat() {
return _.uniq(this.text.split("\n")).join("\n")
}
// 移除行号
removeLineIndex() {
return this.text.replace(new RegExp("^\\s*\\d+\\.?", "gm"), "");
}
// 添加行号
addLineIndex() {
return this.text.split('\n').map((line, index) => `${index + 1}. ${line}`).join('\n')
}
// 行排序
lineSort(type = "asc") {
return _.orderBy(this.text.split(/\r?\n/), (item) => item, type).join("\n");
}
// trim
lineTrim() {
return this.text.split(/\r?\n/).map((item) => item.trim()).join("\n")
}
// 移除空行
filterBlankLine() {
return this.text.split(/\r?\n/).filter((item) => {
return item.trim() !== ""
}).join("\n")
}
// 标点替换
replacePunctuation(type = "zh") {
const zh = ["", "", "", "", "", "", "", "", "", "", "……", "", "", "", "", "", ""]
const en = ['"', '"', "'", "'", ".", ",", ";", ":", "?", "!", "", "-", "~", "(", ")", "<", ">"]
let text = this.text;
for (let i in zh) {
text = text.replace(
new RegExp(regExpQuote(type === "zh" ? en[i] : zh[i]), 'g'),
type === "zh" ? zh[i] : en[i]
);
}
return text
}
// 统计
stat() {
let content = this.text.replace(/\r?\n/g, "\n");
let zh_word = (content.match(/[\u4e00-\u9fa5]/g) || []).length;
let zh_punctuation = (content.match(/[\u3002\uff1f\uff01\uff0c\u3001\uff1b\uff1a\u201c\u201d\u2018\u2019\uff08\uff09\u300a\u300b\u3008\u3009\u3010\u3011\u300e\u300f\u300c\u300d\ufe43\ufe44\u3014\u3015\u2026\u2014\uff5e\ufe4f\uffe5]/g) || []).length;
let int_string = (content.match(/[0-9]/g) || []).length;
let en_string = (content.match(/[A-Za-z]/g) || []).length;
let int_word = (content.match(/\b\d+\b/g) || []).length;
let en_word = (content.match(/\b\w+\b/g) || []).length - int_word;
let en_punctuation = (content.match(/[~`!@#$%^&*()\-_+=|\\[\]{};:"',<.>/?]/g) || []).length;
return {
// 字节数(utf8)
byte_utf8_length: Buffer.byteLength(this.text, 'utf8'),
// 字节数(gbk)
byte_gbk_length: getGbkStrLength(this.text),
// 字符数
string_length: content.replace(/\n/g, "").length,
// 字数
word_length: zh_word + en_word + zh_punctuation + int_word + en_punctuation,
// 中文字数
zh_word,
// 中文标点
zh_punctuation,
// 英文字母
en_string,
// 英文单词
en_word,
// 英文标点
en_punctuation,
// 数字字符
int_string,
// 数字单词
int_word,
// 行数
line_length: this.text ? this.text.split("\n").length : 0
}
}
}
export default (text) => {
return new TextHandle(text)
}
\ No newline at end of file
<template>
<div>
<Row :gutter="10">
<Col span="14">
<Tabs value="content">
<TabPane label="文本内容" name="content">
<Input v-model="current.content" :rows="12" type="textarea" placeholder="内容"></Input>
<option-block style="padding: 0 0">
<FormItem v-if="current.original.length > 0">
<Button type="default" :size="buttonSize"
@click="[current.content,current.original]=[current.original,current.content]">
恢复
</Button>
</FormItem>
<FormItem>
<Dropdown @on-click="(item)=>handle(item)" transfer>
<Button :size="buttonSize" type="primary">
大小写转换
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="upper">全部大写</DropdownItem>
<DropdownItem name="lower">全部小写</DropdownItem>
<DropdownItem name="upperLineStart">行首大写</DropdownItem>
<DropdownItem name="lowerLineStart">行首小写</DropdownItem>
<DropdownItem name="upperStart">词首大写</DropdownItem>
<DropdownItem name="lowerStart">词首小写</DropdownItem>
</DropdownMenu>
</Dropdown>
</FormItem>
<FormItem>
<Dropdown @on-click="(item)=>handle('replacePunctuation',item)" transfer>
<Button :size="buttonSize" type="primary">
中英标点替换
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="en">中 -> 英</DropdownItem>
<DropdownItem name="zh">英 -> 中</DropdownItem>
</DropdownMenu>
</Dropdown>
</FormItem>
<FormItem>
<Dropdown @on-click="(item)=>handle('zhTran',item)" transfer>
<Button :size="buttonSize" type="primary">
简繁转换
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="simplified">繁 -> 简</DropdownItem>
<DropdownItem name="traditional">简 -> 繁</DropdownItem>
</DropdownMenu>
</Dropdown>
</FormItem>
</option-block>
<option-block style="padding: 0 0">
<FormItem>
<Button :size="buttonSize" type="primary" @click="replace.show = true">字符替换</Button>
</FormItem>
<FormItem>
<Button :size="buttonSize" type="primary" @click="handle('lineRemoveRepeat')">行去重
</Button>
</FormItem>
<FormItem>
<Dropdown @on-click="(item)=>handle(item)" transfer>
<Button :size="buttonSize" type="primary">
增/删行号
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="addLineIndex">添加行号</DropdownItem>
<DropdownItem name="removeLineIndex">移除行号</DropdownItem>
</DropdownMenu>
</Dropdown>
</FormItem>
<FormItem>
<Dropdown @on-click="(item)=>handle('lineSort',item)" transfer>
<Button :size="buttonSize" type="primary">
行排序
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="asc">升序</DropdownItem>
<DropdownItem name="desc">降序</DropdownItem>
</DropdownMenu>
</Dropdown>
</FormItem>
<FormItem>
<Dropdown @on-click="(item)=>handle(item)" transfer>
<Button :size="buttonSize" type="primary">
过滤
<Icon type="ios-arrow-down"></Icon>
</Button>
<DropdownMenu slot="list">
<DropdownItem name="lineTrim">过滤行首尾不可见字符(trim)</DropdownItem>
<DropdownItem name="filterBlankLine">过滤多余空行</DropdownItem>
</DropdownMenu>
</Dropdown>
</FormItem>
</option-block>
</TabPane>
</Tabs>
</Col>
<Col span="10">
<Tabs value="right">
<TabPane label="统计" name="right">
<Table :columns="statColumns" stripe border size="small" :data="stat">
</Table>
</TabPane>
<Button @click="statExplain.show = true" type="text" slot="extra">
<Icon type="md-help-circle"/>
</Button>
</Tabs>
</Col>
</Row>
<Modal v-model="statExplain.show" width="600" title="统计说明" class-name="ctool-stat-explain" footer-hide>
<Table :columns="statExplain.columns" stripe size="small" :data="statExplain.data"/>
</Modal>
<Modal v-model="replace.show" width="400" title="字符替换">
<Form label-position="top">
<Row :gutter="16">
<Col span="12">
<FormItem label="查找字符">
<Input v-model="replace.search" :rows="6" type="textarea"></Input>
</FormItem>
</Col>
<Col span="12">
<FormItem label="替换字符">
<Input v-model="replace.replace" :rows="6" type="textarea"></Input>
</FormItem>
</Col>
</Row>
</Form>
<Alert>可输入多行, 按行进行批量替换</Alert>
<div slot="footer">
<Button type="text" @click="replace.show = false">取消</Button>
<Button type="success" @click="replaceRun()">提交</Button>
</div>
</Modal>
</div>
</template>
<script>
import TextHandle from './library/text'
export default {
created() {
this.current = Object.assign(this.current, this.$getToolData("content"))
},
computed: {
stat() {
let stat = TextHandle(this.current.content).stat();
return [
{name: "字符数", value: stat.string_length},
{name: "字节数(utf8/gbk)", value: `${stat.byte_utf8_length} / ${stat.byte_gbk_length}`},
{name: "字数", value: stat.word_length},
{name: "行数", value: stat.line_length},
{name: "(中文)字数/标点", value: `${stat.zh_word} / ${stat.zh_punctuation}`},
{name: "(英文)字母/单词/标点", value: `${stat.en_string} / ${stat.en_word} / ${stat.en_punctuation}`},
{name: "(数字)字符/单词", value: `${stat.int_string} / ${stat.int_word}`},
]
}
},
methods: {
handle(method, ...parameter) {
try {
let result = TextHandle(this.current.content)[method](...parameter)
this.current.original = this.current.content;
this.current.content = result;
this.$saveToolData(this.current);
this.$Message.success("完成")
} catch (e) {
this.$Message.error(e.message)
}
},
replaceRun() {
this.handle('replace', this.replace.search.split(/\r?\n/), this.replace.replace.split(/\r?\n/))
this.replace.show = false
}
},
data() {
return {
replace: {
show: false,
search: "",
replace: ""
},
current: {
content: "",
original: "",
},
statExplain: {
show: false,
columns: [
{
title: '统计项目',
key: 'name',
width: 100
},
{
title: '说明',
key: 'explain',
},
],
data: [
{name: "字节数utf8", explain: "中文字符计3个长度"},
{name: "字节数gbk", explain: "中文字符计2个长度"},
{name: "字符数", explain: "中/英文字符均计1个长度 换行符不计入长度"},
{name: "字数", explain: "中文字数+英文单词数+中文标点数+英文标点数+数字单词数"},
// {name: "中文字数/标点",explain:"-"},
// {name: "英文字母/单词/标点",explain:"-"},
{name: "数字字符", explain: "统计单个数字出现次数 例如:'a1024 1024' 结果为:8"},
{name: "数字单词", explain: "例如:'a1024 1024' 结果为:1 其中:'a1024' 为英文单词 '1024' 为数字单词"},
{name: "行数", explain: "空行也会计入行数"},
]
},
statColumns: [
{
title: '统计项目',
key: 'name',
width: 150
},
{
title: '',
key: 'value',
align: "right"
},
],
buttonSize: "small",
}
},
}
</script>
<style>
.ctool-stat-explain .ivu-modal-body {
padding: 0;
}
</style>
\ No newline at end of file
......@@ -15,7 +15,7 @@
<Checkbox v-model="current.filterLine">过滤中划线(-)</Checkbox>
</FormItem>
<FormItem>
<Checkbox v-model="current.isUpper">大写字母</Checkbox>
<Checkbox v-model="current.isUpper">大写</Checkbox>
</FormItem>
<FormItem>
<Checkbox v-model="current.isAddQuote">添加引号</Checkbox>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册