提交 2b0193de 编写于 作者: Mosher's avatar Mosher

update:更新导入组件

上级 15327e67
......@@ -2,10 +2,11 @@
.app-data-upload-view{
font-family: "Microsoft YaHei";
.main-content{
height: 174px;
height: calc(100% - 68px);
background: #FAFAFB;
border-radius: 4px;
border: 1px dashed #D7D7D7;
margin-top: 10px;
// 上传文件区域
.upload-container{
cursor: pointer;
......@@ -96,6 +97,7 @@
.button-container{
text-align: right;
margin: 15px;
height: 28px;
.el-button{
margin: 0 0 0 8px;
padding: 3px 15px;
......@@ -117,10 +119,10 @@
}
}
}
}
}
//模态样式
.view-default.app-data-upload-modal{
//模态样式
.view-default.app-data-upload-modal{
.app-data-upload-view{
width: 100%;
height: 100%;
......@@ -145,4 +147,4 @@
color: #999999;
font-weight: 600;
}
}
\ No newline at end of file
}
\ No newline at end of file
<template>
<div class="app-data-upload-view" element-loading-background="rgba(57, 57, 57, 0.2)">
<div
class="app-data-upload-view"
v-loading.fullscreen="isUploading"
element-loading-background="rgba(57, 57, 57, 0.2)"
>
<input
ref="inputUpLoad"
type="file"
style="display: none"
accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
@change="fileChange"
@change="importFile"
/>
<div class="main-content">
<div v-if="!selectedFile" class="upload-container" @click="handleUpLoad">
<div v-if="importDataArray.length === 0 && !isUploading && uploadProgress !== 100" class="upload-container" @click="handleUpLoad">
<img class="icon-import" src="@/assets/img/icon-import.svg" />
<span class="select-file-text">{{ $t('components.appDataUploadView.selectfile') }}</span>
</div>
<div v-else class="data-info-container">
<div v-if="!isUploaded" class="message-container">
<div class="success-list" v-if="!isUploading">
{{ $t('components.appDataUploadView.selectfilesucess') }}
</div>
<div class="success-list" v-if="isUploading">
导入过程中,请稍候
</div>
<span class="select-file-text">{{ $t("components.appDataUploadView.selectfile") }}</span>
</div>
<div v-else class="message-container">
<div class="result-list" v-if="Object.keys(responseResult).length > 0">
<ul>
<li class="title"><span>{{ $t('components.appDataUploadView.importresult') }}</span></li>
<li>
<span>
{{ $t('components.appDataUploadView.totaldata') }} {{ responseResult.total }} {{ $t('components.appDataUploadView.total') }}{{ $t('app.commonwords.wrong') }}[{{ responseResult.error }}],{{ $t('app.commonwords.success') }}[{{ responseResult.success }}]
</span>
</li>
<template
v-if="responseResult.errorInfos && responseResult.errorInfos.length !== 0">
<li class="title"><span>{{ $t('components.appDataUploadView.errorinfo') }}</span></li>
<li class="error-item" v-for="(item, index) in responseResult.errorInfos" :key="index">
<span v-if="item.index">{{ $t('components.appDataUploadView.start') }} {{item.index}} {{ $t('components.appDataUploadView.row') }}</span><span v-html="item.errorinfo"></span>
</li>
</template>
</ul>
<div class="data-info-container" v-if="importDataArray.length > 0 || isUploading || uploadProgress == 100">
<el-progress
class="progress"
v-show="isUploading"
:text-inside="true"
:stroke-width="14"
:percentage="uploadProgress"
></el-progress>
<span class="font-class">{{ promptInfo }}</span>
</div>
</div>
</div>
</div>
<el-row class="second-content">
<el-col>
<div class="import-template-message">{{ $t('components.appDataUploadView.datatemplatemessage') }}</div>
<div class="import-template">
<img class="icon-link" src="@/assets/img/icon-link.svg" />
<span style="cursor: pointer" @click="downloadTemp">
{{ viewParams.appDeLogicName + $t('components.appDataUploadView.datatemplate') }}</span
>
</div>
</el-col>
<div class="import-template-download-info" v-if="isUploaded && responseResult.errorfile" @click="downloadFeedbackMsg">
{{ $t('components.appDataUploadView.downloadinfo') }}
</div>
</el-row>
<el-row class="button-container">
<el-button type="primary" @click="handleCancel">{{ $t('components.appDataUploadView.cancel') }}</el-button>
<el-button type="primary" @click="handleCancel">{{ $t("components.appDataUploadView.cancel") }}</el-button>
<el-button
:disabled="!selectedFile"
:disabled="importDataArray.length === 0"
:loading="isUploading"
type="primary"
class="primary-button"
@click="uploadServer"
>{{ $t('components.appDataUploadView.uploadserver') }}</el-button
>{{ $t("components.appDataUploadView.uploadserver") }}</el-button
>
</el-row>
</div>
</template>
<script lang="ts">
import { Environment } from '@/environments/environment';
import { Util } from '@/utils';
import axios from 'axios';
import { Vue, Component, Prop, Watch } from 'vue-property-decorator';
import XLSX from "xlsx";
import CodeListService from "@/codelist/codelist-service";
import { Vue, Component, Prop, Watch } from "vue-property-decorator";
import { Environment } from "@/environments/environment";
import moment from "moment";
import { Util } from "@/utils";
@Component({})
export default class AppDataUploadView extends Vue {
/**
* 传入视图上下文
*
* @type {string}
* @memberof AppDataUploadView
*/
@Prop() protected viewdata!: string;
@Prop() public viewparam: any;
/**
* 传入视图参数
*
* @type {string}
* @memberof AppDataUploadView
*/
@Prop() protected viewparam!: string;
/**
* 视图上下文
* 代码表服务对象
*
* @type {*}
* @type {CodeListService}
* @memberof AppDataUploadView
*/
@Prop() public viewdata: any;
public viewParams: any = {};
public codeListService: CodeListService = new CodeListService({ $store: this.$store });
public context: any = {};
/**
* 实体服务对象
*
* @protected
* @type {EntityService}
* @memberof AppDataUploadView
*/
protected entityService: any;
/**
* 选择文件数据
* 视图参数
*
* @type {*}
* @memberof AppDataUploadView
*/
public selectedFile: any | null = null;
protected viewparams: any = {};
/**
* 是否上传完成
* 导入数据模型
*
* @type {Array<*>}
* @memberof AppDataUploadView
*/
protected importDataModel: Array<any> = [];
/**
* 导入数据集合
*
* @type {Array<*>}
* @memberof AppDataUploadView
*/
public importDataArray: Array<any> = [];
/**
* 导入标识
*
* @type {string}
* @memberof AppDataUploadView
*/
protected importId: string = "";
/**
* 是否已有导入数据
*
* @type {boolean}
* @memberof AppDataUploadView
*/
public isUploaded: boolean = false;
public hasImported: boolean = false;
/**
* 上传进度
* 导入数据识别项属性
*
* @type {number}
* @type {string}
* @memberof AppDataUploadView
*/
public importUniqueItem: string = "";
/**
* 提示信息
*
* @type {string}
* @memberof AppDataUploadView
*/
public uploadedProgress: number = 0;
public promptInfo: string = "";
/**
* 是否上传过程中
* 导入状态
*
* @type {boolean}
* @memberof AppDataUploadView
......@@ -126,12 +150,60 @@ export default class AppDataUploadView extends Vue {
public isUploading: boolean = false;
/**
* 导入结果集合
* 导入成功数据
*
* @type {Array<*>}
* @type {string}
* @memberof AppDataUploadView
*/
public responseResult: any = {};
public importSuccessData: Array<any> = [];
/**
* 导入失败数据
*
* @type {string}
* @memberof AppDataUploadView
*/
public importErrorData: Array<any> = [];
/**
* 读取完成的数据
*
* @type {*}
* @memberof AppDataUploadView
*/
public workBookData: any;
/**
* 所有的代码表
*
* @type {*}
* @memberof AppDataUploadView
*/
public allCodeList: any;
/**
* 属性Map(用作属性转化)
*
* @type {*}
* @memberof AppDataUploadView
*/
public allFieldMap: Map<string, any> = new Map();
/**
* 上传服务器数据切片数
*
* @type {number}
* @memberof AppDataUploadView
*/
public sliceUploadCnt: number = Environment.sliceUploadCnt;
/**
* 上传服务器进度条百分比
*
* @type {number}
* @memberof AppDataUploadView
*/
public uploadProgress: number = 0;
/**
* 视图参数变化
......@@ -140,20 +212,67 @@ export default class AppDataUploadView extends Vue {
* @param {*} oldVal
* @memberof AppDataUploadView
*/
@Watch('viewparam', { immediate: true, deep: true })
@Watch("viewparam", { immediate: true, deep: true })
onParamData(newVal: any, oldVal: any) {
if (newVal) {
this.viewParams = JSON.parse(newVal);
console.log('视图残花', this.viewParams);
Object.assign(this.viewparams, JSON.parse(this.viewparam));
this.initBasic();
}
}
@Watch('viewdata', { immediate: true, deep: true })
onViewDataChange(newVal: any, oldVal: any) {
if (newVal) {
this.context = JSON.parse(newVal);
console.log('上下文', this.context);
/**
* 初始化基础数据
*
* @memberof AppDataUploadView
*/
public async initBasic() {
if (this.viewparams.importId) {
this.importId = this.viewparams.importId;
}
if (this.viewparams.importData) {
this.importDataModel = Object.values(this.viewparams.importData);
this.bubbleSort(this.importDataModel, this.importDataModel.length);
}
this.importDataModel.forEach((item: any) => {
if (item.isuniqueitem) {
this.importUniqueItem = item.headername;
}
this.allFieldMap.set(item.headername, item);
});
if (this.viewparams.serviceName) {
this.entityService = await window.entityServiceRegister.getService(this.viewparams.serviceName.toLowerCase());
}
//获取代码表值
this.allCodeList = await this.getChartAllCodeList();
}
/**
* 冒泡排序
*
* @param {*} newVal
* @param {*} oldVal
* @memberof AppDataUploadView
*/
public bubbleSort(array: Array<any>, length: number) {
for (let i = 0; i < length; i++) {
for (let j = 0; j < length - i - 1; j++) {
if (array[j].order > array[j + 1].order) {
let temp: any;
temp = array[j + 1];
array[j + 1] = array[j];
array[j] = temp;
}
}
}
}
/**
* 下载导入数据模板
*
* @memberof AppDataUploadView
*/
public downloadTemp() {
this.importExcel(this.viewparams.appDeLogicName + this.$t("components.appDataUploadView.datatemp"), []);
}
/**
......@@ -162,247 +281,288 @@ export default class AppDataUploadView extends Vue {
* @memberof AppDataUploadView
*/
public handleUpLoad() {
this.importSuccessData = [];
(this.$refs.inputUpLoad as any).click();
}
/**
* 上传服务器
*
* @memberof AppDataUploadView
*/
public uploadServer() {
if (this.importDataArray.length == 0 || !this.entityService) {
return;
}
let tempDataArray: Array<any> = [];
this.transformData(this.importDataArray, tempDataArray);
this.hasImported = true;
this.isUploading = true;
this.uploadProgress = 0;
this.importDataArray = [];
this.sliceUploadService(tempDataArray, 0);
}
/**
* 数据切片上传
*
* @memberof AppDataUploadView
*/
public sliceUploadService(dataArray: Array<any>, cnt: number) {
if (cnt > dataArray.length) {
this.isUploading = false;
this.uploadProgress = 100;
this.promptInfo = this.$t("components.appDataUploadView.completed") as string;
return;
}
let sliceArray: Array<any> = [];
if (dataArray) {
sliceArray = dataArray.slice(cnt, cnt + this.sliceUploadCnt);
}
try {
this.entityService
.ImportData(this.viewdata, { name: this.importId, importData: sliceArray })
.then((res: any) => {
const result: any = res.data;
if (result && result.rst !== 0) {
this.promptInfo = this.$t("components.appDataUploadView.importfailed") as string;
this.isUploading = false;
return;
}
this.importSuccessData = result.data;
this.promptInfo = this.$t("components.appDataUploadView.completed") as string;
this.uploadProgress = Number(((cnt / dataArray.length) * 100).toFixed(2));
this.sliceUploadService(dataArray, cnt + this.sliceUploadCnt);
})
.catch((error: any) => {
this.isUploading = false;
this.promptInfo = this.$t("components.appDataUploadView.importfailed") as string;
});
} catch (error: any) {
this.isUploading = false;
this.promptInfo = this.$t("components.appDataUploadView.importfailed") as string;
}
}
/**
* 取消
*
* @memberof AppDataUploadView
*/
public handleCancel() {
this.$emit('close', []);
this.$emit("close", []);
}
/**
* 导出excel
*
* @memberof AppDataUploadView
*/
public async importExcel(filename: string, _data: any) {
const tHeader: Array<any> = [];
this.importDataModel.forEach((item: any) => {
tHeader.push(item.headername);
});
this.$export.exportExcel().then((excel: any) => {
excel.export_json_to_excel({
header: tHeader, //表头 必填
data: [], //具体数据 必填
filename: filename, //非必填
autoWidth: true, //非必填
bookType: "xlsx", //非必填
});
});
}
/**
* 确认
*
* @memberof AppDataUploadView
*/
public handleOK() {
this.$emit("close", this.importSuccessData);
}
/**
* 文件数据变化
* 导入Excel
*
* @memberof AppDataUploadView
*/
public fileChange($event: any) {
public importFile($event: any) {
let obj = $event.target || $event.srcElement;
if (!obj.files) {
return;
}
this.selectedFile = obj.files && obj.files.length > 0 ? obj.files[0] : {};
let f = obj.files[0];
let reader = new FileReader();
reader.onload = (e: any) => {
let data = e.target.result;
this.workBookData = XLSX.read(data, { type: "binary", cellDates: true });
let xlsxData = XLSX.utils.sheet_to_json(this.workBookData.Sheets[this.workBookData.SheetNames[0]]);
let list1 = this.getFirstRow(this.workBookData);
xlsxData = this.addXlsxData(xlsxData, list1);
this.importDataArray = Util.deepCopy(xlsxData);
this.promptInfo = `${this.$t('components.appDataUploadView.selectfilesucess')}, ${this.$t('components.appDataUploadView.totaldata')} ${this.importDataArray.length} ${this.$t('components.appDataUploadView.total')}`;
(this.$refs.inputUpLoad as any).value = "";
};
reader.readAsBinaryString(f);
}
/**
* 设置UI状态
* 获取excel第一行的内容
*
* @memberof AppDataUploadView
*/
public setUIState(uploadedProgress: number, isUploading: boolean, isUploaded: boolean, result: any = {}) {
this.uploadedProgress = uploadedProgress;
this.isUploading = isUploading;
this.isUploaded = isUploaded;
this.responseResult = result;
public getFirstRow(wb: any) {
//// 读取的excel单元格内容
let wbData = wb.Sheets[wb.SheetNames[0]];
// 匹配excel第一行的内容
let re = /^[A-Z]1$/;
let temparr = [];
// excel第一行内容赋值给数组
for (let key in wbData) {
if (wbData.hasOwnProperty(key)) {
if (re.test(key)) {
temparr.push(wbData[key].h);
}
}
}
return temparr;
}
/**
* 下载导入数据模板
* 增加对应字段空白内容
*
* @memberof AppDataUploadView
*/
public downloadTemp() {
let requestUrl: string = '';
if (this.viewdata && this.context.srfparentkey && this.context.srfparentdename && this.context.srfparentdename !== this.viewdata.appEntityName) {
requestUrl += `/${Util.srfpluralize(this.viewdata.srfparentdename)}/${this.viewdata.srfparentkey}`;
}
requestUrl += `/${Util.srfpluralize(this.viewParams.serviceName)}/importtemplate`;
if (this.viewParams.importId) {
requestUrl += `?srfimporttag=${this.viewParams.importId}`;
}
axios({
url: requestUrl,
method: 'get',
responseType: 'blob',
}).then((response: any) => {
if (response.status == 200) {
let fileName = response.headers['content-disposition']
.split(';')
.find((str: string) => str.indexOf('filename=') != -1)
.slice(9);
fileName = decodeURIComponent(fileName);
let blob = new Blob([response.data], { type: 'application/vnd.ms-excel' });
let elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href); // 释放URL 对象
document.body.removeChild(elink);
public addXlsxData(xlsxData: any, list1: any) {
// 空白字段替换值
let addData = null;
for (let i = 0; i < xlsxData.length; i++) {
// 要被JSON的数组
for (let j = 0; j < list1.length; j++) {
// excel第一行内容
if (!xlsxData[i][list1[j]]) {
xlsxData[i][list1[j]] = addData;
}
});
}
}
return xlsxData;
}
/**
* 下载导入反馈信息
* 获取图表所需代码表
*
* @memberof AppDataUploadView
*/
public downloadFeedbackMsg() {
if(!this.responseResult || !this.responseResult.errorfile || !this.responseResult.errorfile.fileid){
this.$Notice.error({ desc: this.$t('components.appDataUploadView.downloaderror') as string });
return;
}
let downloadUrl: string = `${Environment.ExportFile}/${this.responseResult.errorfile.folder}/${this.responseResult.errorfile.fileid}`;
const headers = {};
axios({
method: 'get',
url: downloadUrl,
headers: headers,
responseType: 'blob'
}).then((response: any) => {
if (!response || response.status != 200) {
this.$Notice.error({ desc: this.$t('components.appDataUploadView.downloaderror') as string });
return;
public async getChartAllCodeList() {
let codeListMap: Map<string, any> = new Map();
if (Object.values(this.importDataModel).length > 0) {
await Object.values(this.importDataModel).forEach(async (singleDataModel: any) => {
if (singleDataModel.codelist) {
let tempCodeListMap: Map<any, any> = new Map();
let res: any = await this.getCodeList(singleDataModel.codelist);
if (res && res.length > 0) {
res.forEach((codeListItem: any) => {
tempCodeListMap.set(codeListItem.value, codeListItem.text);
});
}
// 请求成功,后台返回的是一个文件流
if (response.data) {
// 获取文件名
const filename = `导入错误${this.responseResult.errorfile.fileid}.xlsx`;
let filetype = this.calcFilemime('xlsx');
// 用blob对象获取文件流
let blob = new Blob([response.data], { type: filetype });
// 通过文件流创建下载链接
var href = URL.createObjectURL(blob);
// 创建一个a元素并设置相关属性
let a = document.createElement('a');
a.href = href;
a.download = filename;
// 添加a元素到当前网页
document.body.appendChild(a);
// 触发a元素的点击事件,实现下载
a.click();
// 从当前网页移除a元素
document.body.removeChild(a);
// 释放blob对象
URL.revokeObjectURL(href);
} else {
this.$Notice.error({ desc: this.$t('components.appDataUploadView.downloaderror') as string });
codeListMap.set(singleDataModel.codelist.tag, tempCodeListMap);
}
}).catch((error: any) => {
console.error(error);
});
}
/**
* 计算文件mime类型
*
* @param filetype 文件后缀
* @memberof AppDataUploadView
*/
public calcFilemime(filetype: string): string {
let mime = "application/vnd.ms-excel";
switch (filetype) {
case ".xlsx":
mime = "application/vnd.ms-excel";
break;
case ".wps":
mime = "application/kswps";
break;
case ".doc":
mime = "application/msword";
break;
case ".docx":
mime = "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
break;
case ".txt":
mime = "text/plain";
break;
case ".zip":
mime = "application/zip";
break;
case ".png":
mime = "image/png";
break;
case ".gif":
mime = "image/gif";
break;
case ".jpeg":
mime = "image/jpeg";
break;
case ".jpg":
mime = "image/jpeg";
break;
case ".rtf":
mime = "application/rtf";
break;
case ".avi":
mime = "video/x-msvideo";
break;
case ".gz":
mime = "application/x-gzip";
break;
case ".tar":
mime = "application/x-tar";
break;
}
return mime;
return codeListMap;
}
/**
* 上传服务器
* 获取代码表
*
* @returns {Promise<any>}
* @memberof AppDataUploadView
*/
public uploadServer() {
if (!this.selectedFile) {
return;
public getCodeList(codeListObject: any): Promise<any> {
return new Promise((resolve: any, reject: any) => {
if (codeListObject.tag && Object.is(codeListObject.type, "STATIC")) {
const codelist = this.$store.getters.getCodeList(codeListObject.tag);
if (codelist) {
resolve([...JSON.parse(JSON.stringify(codelist.items))]);
} else {
console.log(`----${codeListObject.tag}----${this.$t("app.commonWords.codeNotExist") as string}`);
}
try {
let requestUrl: string = '';
this.setUIState(0, true, false);
if (this.viewdata && this.viewdata.srfparentkey && this.viewdata.srfparentdename) {
requestUrl += `/${Util.srfpluralize(this.viewdata.srfparentdename)}/${this.viewdata.srfparentkey}`;
}
requestUrl += `/${Util.srfpluralize(this.viewParams.serviceName)}/importdata2`;
if (this.viewParams.importId) {
requestUrl += `?srfimporttag=${this.viewParams.importId}`;
}
const data = new FormData();
data.append('file', this.selectedFile);
axios
.post(requestUrl, data, {
headers: { 'Content-Type': 'multipart/form-data' },
onUploadProgress: (progressEvent: any) => {
this.uploadedProgress = Math.floor((progressEvent.loaded / progressEvent.total) * 100);
},
})
} else if (codeListObject.tag && Object.is(codeListObject.type, "DYNAMIC")) {
this.codeListService
.getItems(codeListObject.tag)
.then((res: any) => {
const result: any = {};
if (res && res.status && res.status == 200) {
const { data: data } = res;
const {errorinfo,success,total,errorfile} = data;
result.total = total ? Number(total) : 0;
result.success = success ? Number(success) : 0;
result.errorfile = errorfile;
if(errorinfo && Object.keys(errorinfo).length >0){
result.error = Object.keys(errorinfo).length;
result.errorInfos = [];
Object.keys(errorinfo).forEach((item:any) =>{
result.errorInfos.push({index: Number(item) + 1, errorinfo: errorinfo[item].errorInfo});
resolve(res);
})
.catch((error: any) => {
console.log(
`----${codeListObject.tag}----${this.$t("app.commonWords.codeNotExist") as string}`
);
});
}
});
}
this.setUIState(0, false, true, result);
}).catch((error: any) => {
const errorResult: any = {};
if (error && error.status && error.status !== 200) {
errorResult.errorInfos = [];
errorResult.errorInfos.push({ errorInfo: error.data && error.data.message ? error.data.message : '' });
/**
* 转化数据
*
* @memberof AppDataUploadView
*/
public transformData(data: Array<any>, result: Array<any>) {
data.forEach((item: any) => {
let curObject: any = {};
Object.keys(item).forEach((ele: any) => {
// todo XLSX读取时间为国际时间(东8区)+8H转为标准时间
if (item[ele] instanceof Date) {
const tempDate: Date = item[ele];
item[ele] = moment(tempDate)
.add(8, "h")
.format("YYYY-MM-DD HH:mm:ss");
}
this.setUIState(0, false, true, errorResult);
if (this.allFieldMap.get(ele).codelist) {
let codelistTag: string = this.allFieldMap.get(ele).codelist.tag;
let codelistIsNumber: boolean = this.allFieldMap.get(ele).codelist.isnumber;
let curCodeList: any = this.transCodeList(codelistTag, codelistIsNumber, true);
Object.defineProperty(curObject, this.allFieldMap.get(ele).name, {
value: curCodeList.get(item[ele]),
writable: true,
enumerable: true,
configurable: true,
});
} catch (error: any) {
this.setUIState(0, false, true, { errorInfos:[{errorInfo: error.data}] });
} else {
Object.defineProperty(curObject, this.allFieldMap.get(ele).name, {
value: item[ele],
writable: true,
enumerable: true,
configurable: true,
});
}
});
result.push(curObject);
});
}
/**
* 翻译代码表
*
* @memberof AppDataUploadView
*/
public transCodeList(codeListTag: string, codelistIsNumber: boolean, isTransform: boolean) {
let curCodeList: any = this.allCodeList.get(codeListTag);
if (isTransform) {
let tempCodelist: Map<string, string> = new Map();
curCodeList.forEach((item: string, key: string) => {
let value: any = codelistIsNumber ? Number(key) : key;
tempCodelist.set(item, value);
});
curCodeList = tempCodelist;
}
return curCodeList;
}
}
</script>
<style lang='less'>
@import './app-data-upload.less';
<style lang="less">
@import "./app-data-upload.less";
</style>
Markdown 格式
0% or
您添加了 0 到此讨论。请谨慎行事。
先完成此消息的编辑!
想要评论请 注册