提交 d191c3fa authored 作者: zs's avatar zs

edit

上级 0892dbfe
...@@ -45,7 +45,7 @@ export default { ...@@ -45,7 +45,7 @@ export default {
,inspectionTime ,inspectionTime
,isException ,isException
,synFlag ,synFlag
,createTime,delFlag ,createTime,delFlag,inspectionData
FROM ${table.inspectionRecordName} FROM ${table.inspectionRecordName}
where 1=1 where 1=1
${data.inspectionType ? `AND inspectionType = '${data.inspectionType}'` : ''} ${data.inspectionType ? `AND inspectionType = '${data.inspectionType}'` : ''}
......
...@@ -13,7 +13,7 @@ export default { ...@@ -13,7 +13,7 @@ export default {
// app初始化 // app初始化
async init() { async init() {
const BASE_PATH = "_documents/data/asmzx/pad"; // 基础路径
// uni.showLoading({ // uni.showLoading({
// title: '正在初始化...', // title: '正在初始化...',
// }) // })
...@@ -25,7 +25,7 @@ export default { ...@@ -25,7 +25,7 @@ export default {
await this.initSqlLite(sqllitedb); // 确保表被创建 await this.initSqlLite(sqllitedb); // 确保表被创建
console.log('Tables created.'); console.log('Tables created.');
await ioUtil.createDir(BASE_PATH, '发送数据')
let isInit = await SqlliteDbUtil.checkIfDataImported(sqllitedb); // 确保表存在后再查询 let isInit = await SqlliteDbUtil.checkIfDataImported(sqllitedb); // 确保表存在后再查询
console.log('Data imported:', isInit); console.log('Data imported:', isInit);
if (!isInit) { if (!isInit) {
...@@ -94,20 +94,14 @@ export default { ...@@ -94,20 +94,14 @@ export default {
// 初始化目录 // 初始化目录
async initDir() { async initDir() {
try { try {
const _www = _Path.getPrivateWWW() const downloadEntry = await ioUtil.getFileEntry()
const _doc = _Path.getPrivateDOC()
const _download = _Path.getDownloads()
const _temp = _Path.getTempPath()
const wwwEntry = await ioUtil.getFileEntry(_www)
const docEntry = await ioUtil.getFileEntry(_doc)
const downloadEntry = await ioUtil.getFileEntry(_download)
if (downloadEntry) { if (downloadEntry) {
await ioUtil.createDir(downloadEntry.fullPath, 'base') await ioUtil.createDir(BASE_PATH, 'base')
await ioUtil.createDir(downloadEntry.fullPath, 'export') await ioUtil.createDir(BASE_PATH, 'export')
await ioUtil.createDir(downloadEntry.fullPath, 'import') await ioUtil.createDir(BASE_PATH, 'import')
await ioUtil.createDir(downloadEntry.fullPath, 'doc') await ioUtil.createDir(BASE_PATH, 'doc')
await ioUtil.createDir(downloadEntry.fullPath, 'error') await ioUtil.createDir(BASE_PATH, 'error')
} }
// 获取设备名称 // 获取设备名称
...@@ -118,7 +112,7 @@ export default { ...@@ -118,7 +112,7 @@ export default {
brand: e.deviceBrand, brand: e.deviceBrand,
deviceModel: e.deviceModel, deviceModel: e.deviceModel,
} }
const deviceFilePath = downloadEntry.fullPath + 'name.txt' const deviceFilePath = BASE_PATH + 'name.txt'
await ioUtil.removeExistFile(deviceFilePath) await ioUtil.removeExistFile(deviceFilePath)
await ioUtil.writeFile('name.txt', downloadEntry, JSON.stringify(deviceData)) await ioUtil.writeFile('name.txt', downloadEntry, JSON.stringify(deviceData))
}, },
......
<template> <template>
<view class="synchronous-dialog"> <view class="synchronous-dialog">
<view class="synchronous-content"> <view class="synchronous-content">
<view class="row-item"> <view class="row-item">
<text class="title">待打包数据</text> <text class="title">待打包数据</text>
</view> </view>
<view class="row-item count-num"> <view class="row-item count-num">
<text class="num">{{ list.length }}</text> <text class="num">{{ list.length }}</text>
<text></text> <text></text>
</view> </view>
<view class="operating-instructions"> <view class="operating-instructions">
<view class="title">操作说明:</view> <view class="title">操作说明:</view>
<view class="instructions-item"> <view class="instructions-item">
1、在PAD端,点击“数据打包”,做好同步准备。 1、在PAD端,点击“数据打包”,做好同步准备。
</view> </view>
<view class="instructions-item"> <view class="instructions-item">
2、完成打包后,将PAD直联PC机,等待PC机自动导入需同步记录。 2、完成打包后,将PAD直联PC机,等待PC机自动导入需同步记录。
</view> </view>
<view class="instructions-item"> <view class="instructions-item">
3、PC端自动导入完成后,请点击“同步数据”,同步成功后,列表自动刷新展示同步数据;且PAD同步的数据不支持修改。 3、PC端自动导入完成后,请点击“同步数据”,同步成功后,列表自动刷新展示同步数据;且PAD同步的数据不支持修改。
</view> </view>
</view> </view>
<view class="row-item bottom-row"> <view class="row-item bottom-row">
<button class="button" :loading="loading" @click="clickHandle"> <button class="button" :loading="loading" @click="clickHandle">
数据打包 数据打包
</button> </button>
</view> </view>
<!-- 关闭按钮 --> <!-- 关闭按钮 -->
<div class="close-button"> <div class="close-button">
<text class="iconfont icon-shibai1" @click="close"></text> <text class="iconfont icon-shibai1" @click="close"></text>
</div> </div>
</view> </view>
</view> </view>
</template> </template>
<script> <script>
import { import {
SYNCHRONIZE_DATA_PAD, SYNCHRONIZE_DATA_PAD,
checkAndCreateDirectory, checkAndCreateDirectory,
createFileWithPlusIO, createFileWithPlusIO,
setSm2, setSm2,
USER_FILE_NAME, USER_FILE_NAME,
getUserList, getUserList,
} from "@/utils/systemCofig"; zipCompress
import { findPhotosUrls } from "@/utils/index.js"; } from "@/utils/systemCofig";
import { copySelectedFiles } from "@/utils/plus.js"; import {
findPhotosUrls
import { } from "@/utils/index.js";
writeInspectionData, import {
copyDirectory, copySelectedFiles
deleteAllFilesInDirectory, } from "@/utils/plus.js";
addLog,
getLogContent, import {
LOG_TYPE_ENUM, writeInspectionData,
} from "@/utils/IoReadingAndWriting.js"; copyDirectory,
import moment from "moment"; deleteAllFilesInDirectory,
import { getAllInspections } from "@/request/index.js"; addLog,
import { Base64 } from "js-base64"; getLogContent,
import inspectApi from "@/api/inspect"; LOG_TYPE_ENUM,
export default { } from "@/utils/IoReadingAndWriting.js";
props: { import moment from "moment";
list: { import {
type: Array, getAllInspections
default: () => {}, } from "@/request/index.js";
}, import {
}, Base64
components: {}, } from "js-base64";
data() { import inspectApi from "@/api/inspect";
return { import inspect from "@/api/inspect";
loading: false, export default {
notSynchronizationList: [], // 未同步数据 props: {
allList: [], // 所有数据 list: {
}; type: Array,
}, default: () => {},
computed: { },
userInfo() { },
return this.$store.state.now_user; components: {},
}, data() {
}, return {
loading: false,
watch: { notSynchronizationList: [], // 未同步数据
list(newVal) { allList: [], // 所有数据
console.log("newVal", newVal); };
this.init(); },
}, computed: {
}, userInfo() {
// async mounted() { return this.$store.state.now_user;
// const temp = []; },
// this.list.forEach((item) => { },
// temp.push(...(item.list || []));
// }); watch: {
// console.log("this.list", this.list); list(newVal) {
// console.log("temp", temp); console.log("newVal", newVal);
// this.notSynchronizationList = temp.filter( this.init();
// (item) => item.synchronization == 0 && item.isSign === true },
// ); },
// async mounted() {
// getAllInspections().then((res) => { // const temp = [];
// this.allList = res; // this.list.forEach((item) => {
// }); // temp.push(...(item.list || []));
// }, // });
methods: { // console.log("this.list", this.list);
init() { // console.log("temp", temp);
const temp = []; // this.notSynchronizationList = temp.filter(
this.notSynchronizationList = this.list; // (item) => item.synchronization == 0 && item.isSign === true
// );
getAllInspections().then((res) => {
this.allList = res; // getAllInspections().then((res) => {
}); // this.allList = res;
}, // });
close() { // },
this.$emit("close"); methods: {
}, init() {
const temp = [];
/** this.notSynchronizationList = this.list;
* 1.生成两个文件. 机房文件 和 井道文件
* 2. 更新巡检数据状态 synchronization 置为 1 getAllInspections().then((res) => {
* 3. 写入数据 this.allList = res;
* 读取上一次打包的文件, 复制到 [ history ] 文件夹中 });
*/ },
async clickHandle() { close() {
if (this.loading) return; this.$emit("close");
const directoryPath = `${SYNCHRONIZE_DATA_PAD}/发送数据`; },
const targetDirectoryPath = `${SYNCHRONIZE_DATA_PAD}/history`;
/**
checkAndCreateDirectory(directoryPath).then(() => { * 1.生成两个文件. 机房文件 和 井道文件
copyDirectory(directoryPath, targetDirectoryPath) * 2. 更新巡检数据状态 synchronization 置为 1
.then(() => { * 3. 写入数据
return deleteAllFilesInDirectory(directoryPath); * 读取上一次打包的文件, 复制到 [ history ] 文件夹中
}) */
.then(() => { async clickHandle() {
this.coverData(); if (this.loading) return;
}) const directoryPath = `${SYNCHRONIZE_DATA_PAD}/发送数据`;
.catch((error) => { const targetDirectoryPath = `${SYNCHRONIZE_DATA_PAD}/history`;
uni.showToast({
title: error, // checkAndCreateDirectory(directoryPath).then(() => {
icon: "none", copyDirectory(directoryPath, targetDirectoryPath)
duration: 1000, .then(() => {
}); return deleteAllFilesInDirectory(directoryPath);
}); })
}); .then(() => {
}, this.coverData();
copyPhotos() { })
const urls = findPhotosUrls(this.list).map((url) => url.split("/").pop()); .catch((error) => {
console.log("urls", urls); uni.showToast({
return copySelectedFiles( title: error,
"_documents/data/photos", icon: "none",
`${SYNCHRONIZE_DATA_PAD}/发送数据/${this.userInfo.user}`, duration: 1000,
urls });
); });
}, // });
// 处理数据 },
coverData() { copyPhotos() {
const userName = this.$store.state.now_user.user; this.list.forEach(e => {
const notSynchronizationList = this.notSynchronizationList; e.inspectionData = JSON.parse(e.inspectionData) || '{}'
const allList = this.allList; })
console.log("窗口allList", allList); const urls = findPhotosUrls(this.list).map((url) => url.split("/").pop());
console.log( console.log("urls", urls);
"窗口this.notSynchronizationList", return copySelectedFiles(
this.notSynchronizationList "_documents/data/photos",
); `${SYNCHRONIZE_DATA_PAD}/发送数据`,
urls
let timeStr = moment().format("yyyy_MM_DD_hh_mm_ss"); );
},
const JFXJ_DATA = notSynchronizationList // 处理数据
.filter((item) => item.inspectionType == 1) coverData() {
.map((item) => { const userName = this.$store.state.now_user.user;
return item; const notSynchronizationList = this.notSynchronizationList;
}); // 机房数据类型是 1 const allList = this.allList;
console.log("窗口allList", allList);
const JDXJ_DATA = notSynchronizationList console.log(
.filter((item) => item.inspectionType == 2) "窗口this.notSynchronizationList",
.map((item) => { this.notSynchronizationList
return item; );
}); // 井道数据类型是 2
console.log(5151, JFXJ_DATA, JDXJ_DATA); let timeStr = moment().format("yyyy_MM_DD_hh_mm_ss");
this.loading = true; const JFXJ_DATA = notSynchronizationList
const tmepList = []; .filter((item) => item.inspectionType == 1)
.map((item) => {
if (JFXJ_DATA.length) { return item;
let JFXJ_DATA_FILE_NAME = `${userName}_JFXJ_${timeStr}.txt`; }); // 机房数据类型是 1
tmepList.push(this.packedData(JFXJ_DATA, JFXJ_DATA_FILE_NAME));
} const JDXJ_DATA = notSynchronizationList
.filter((item) => item.inspectionType == 2)
JDXJ_DATA.forEach((item, index) => { .map((item) => {
let JDXJ_DATA_FILE_NAME = `${userName}_JDXJ_${timeStr}_${index}.txt`; return item;
tmepList.push(this.packedData(item, JDXJ_DATA_FILE_NAME)); }); // 井道数据类型是 2
}); console.log(5151, JFXJ_DATA, JDXJ_DATA);
console.log("同步窗口"); this.loading = true;
console.log("同步窗口", tmepList); const tmepList = [];
Promise.all(tmepList)
.then(() => { if (JFXJ_DATA.length) {
// 更新巡检数据状态 let JFXJ_DATA_FILE_NAME = `${userName}_JFXJ_${timeStr}.txt`;
const synchronizationUids = notSynchronizationList.map((item) => { tmepList.push(this.packedData(JFXJ_DATA, JFXJ_DATA_FILE_NAME));
item.synchronization = 1; }
return item.uid;
}); JDXJ_DATA.forEach((item, index) => {
let JDXJ_DATA_FILE_NAME = `${userName}_JDXJ_${timeStr}_${index}.txt`;
const userData = {}; tmepList.push(this.packedData(item, JDXJ_DATA_FILE_NAME));
});
allList.forEach((item) => {
if (synchronizationUids.includes(item.uid)) { console.log("同步窗口");
item.synchronization = 1; console.log("同步窗口", tmepList);
} Promise.all(tmepList)
if (userData[item.createByName]) { .then(() => {
userData[item.createByName].push(item); // 更新巡检数据状态
} else { const synchronizationUids = notSynchronizationList.map((item) => {
userData[item.createByName] = [item]; item.synchronization = 1;
} return item.uid;
}); });
this.$store.commit("SET_ALL_DATA", allList);
const userData = {};
const keys = Object.keys(userData);
const promiseArr = keys.map((key) => { allList.forEach((item) => {
const val = userData[key]; if (synchronizationUids.includes(item.uid)) {
return writeInspectionData(val, key); item.synchronization = 1;
}); }
if (userData[item.createByName]) {
Promise.all(promiseArr) userData[item.createByName].push(item);
.then(async () => { } else {
setTimeout(async () => { userData[item.createByName] = [item];
uni.showToast({ }
title: "打包成功", });
icon: "none", this.$store.commit("SET_ALL_DATA", allList);
duration: 2000,
}); const keys = Object.keys(userData);
let ids = this.list.map((e) => e.id); const promiseArr = keys.map((key) => {
await inspectApi.updateSyncData(ids); const val = userData[key];
this.close(); return writeInspectionData(val, key);
this.loading = false; });
// 生成日志 Promise.all(promiseArr)
const logContent = getLogContent( .then(async () => {
LOG_TYPE_ENUM.sys, setTimeout(async () => {
"", uni.showToast({
"同步模块" title: "打包成功",
); icon: "none",
const log_list = this.$store.state.log_list; duration: 2000,
log_list.push(logContent); });
this.$store.commit("SET_LOG_LIST", log_list); let ids = this.list.map((e) => e.id);
addLog(log_list).then((res) => {}); // 复制照片
await this.copyPhotos();
// 更新同步时间 await zipCompress(`${SYNCHRONIZE_DATA_PAD}/发送数据`,
this.updateSysTime(); `${SYNCHRONIZE_DATA_PAD}/fssj/${userName}_JFXJ_${timeStr}.zip`
);
// 复制照片 // zip
await this.copyPhotos(); await inspectApi.updateSyncData(ids);
// zip this.close();
}, 2 * 1000); this.loading = false;
})
.catch((error) => { // 生成日志
this.loading = false; const logContent = getLogContent(
LOG_TYPE_ENUM.sys,
uni.showToast({ "",
title: error, "同步模块"
icon: "none", );
duration: 2000, const log_list = this.$store.state.log_list;
}); log_list.push(logContent);
}); this.$store.commit("SET_LOG_LIST", log_list);
}) addLog(log_list).then((res) => {});
.catch(() => {
setTimeout(() => { // 更新同步时间
uni.showToast({ this.updateSysTime();
title: "打包失败",
icon: "none",
duration: 2000, }, 2 * 1000);
}); })
this.loading = false; .catch((error) => {
}, 2 * 1000); this.loading = false;
});
}, uni.showToast({
title: error,
// 打包文件 icon: "none",
packedData(content, fileName) { duration: 2000,
// const fileContent = setSm2(content); });
const fileContent = Base64.encode(JSON.stringify(content)); });
})
console.log("packedData", fileContent); .catch(() => {
return createFileWithPlusIO( setTimeout(() => {
`${SYNCHRONIZE_DATA_PAD}/发送数据`, uni.showToast({
fileName, title: "打包失败",
fileContent icon: "none",
); duration: 2000,
}, });
this.loading = false;
// 更新最近一次同步时间 }, 2 * 1000);
updateSysTime() { });
getUserList().then((personList) => { },
const now_user = this.$store.state.now_user;
const key = personList.findIndex(
(item) => item.userId == now_user.userId // 使用示例
); // 打包文件
// 更新用户同步时间 packedData(content, fileName) {
const userInfo = personList[key]; // const fileContent = setSm2(content);
const LastSynchronizationTime = moment().format("yyyy-MM-DD HH:mm"); const fileContent = Base64.encode(JSON.stringify(content));
personList[key].LastSynchronizationTime = LastSynchronizationTime;
userInfo.LastSynchronizationTime = LastSynchronizationTime; console.log("packedData", fileContent);
return createFileWithPlusIO(
this.$store.commit("SET_USER", userInfo); `${SYNCHRONIZE_DATA_PAD}/发送数据`,
uni.setStorageSync("last_time", userInfo.LastSynchronizationTime || ""); fileName,
fileContent
// 更新用户数据 );
const fileContent = JSON.stringify( },
Base64.encode(JSON.stringify(personList))
); // 更新最近一次同步时间
uni.setStorage({ updateSysTime() {
key: "user_data", getUserList().then((personList) => {
data: JSON.stringify(personList), const now_user = this.$store.state.now_user;
fail: (error) => { const key = personList.findIndex(
console.log("APP.vue 存储数据失败", error); (item) => item.userId == now_user.userId
}, );
}); // 更新用户同步时间
createFileWithPlusIO(SYNCHRONIZE_DATA_PAD, USER_FILE_NAME, fileContent) const userInfo = personList[key];
.then(() => { const LastSynchronizationTime = moment().format("yyyy-MM-DD HH:mm");
console.log("---用户数据更新成功"); personList[key].LastSynchronizationTime = LastSynchronizationTime;
}) userInfo.LastSynchronizationTime = LastSynchronizationTime;
.catch((error) => {
console.log("---用户数据更新失败", error); this.$store.commit("SET_USER", userInfo);
}); uni.setStorageSync("last_time", userInfo.LastSynchronizationTime || "");
});
}, // 更新用户数据
}, const fileContent = JSON.stringify(
}; Base64.encode(JSON.stringify(personList))
);
uni.setStorage({
key: "user_data",
data: JSON.stringify(personList),
fail: (error) => {
console.log("APP.vue 存储数据失败", error);
},
});
createFileWithPlusIO(SYNCHRONIZE_DATA_PAD, USER_FILE_NAME, fileContent)
.then(() => {
console.log("---用户数据更新成功");
})
.catch((error) => {
console.log("---用户数据更新失败", error);
});
});
},
},
};
</script> </script>
<style scoped lang="less"> <style scoped lang="less">
.synchronous-dialog { .synchronous-dialog {
position: fixed; position: fixed;
z-index: 999; z-index: 999;
top: 0; top: 0;
left: 0; left: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background: rgba(0, 0, 0, 0.5); background: rgba(0, 0, 0, 0.5);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
.synchronous-content {
padding: 3% 20px 32px 24px; .synchronous-content {
width: 400px; padding: 3% 20px 32px 24px;
height: 60%; width: 400px;
box-sizing: border-box; height: 60%;
background-image: linear-gradient( box-sizing: border-box;
-6deg, background-image: linear-gradient(-6deg,
#f9ffe7 0%, #f9ffe7 0%,
#ffffff 12%, #ffffff 12%,
#fcfeff 73%, #fcfeff 73%,
#ccf1ff 100% #ccf1ff 100%);
); border: 0.4px solid rgba(224, 224, 224, 1);
border: 0.4px solid rgba(224, 224, 224, 1); border-radius: 12px;
border-radius: 12px; position: relative;
position: relative;
.row-item {
.row-item { display: flex;
display: flex; align-items: center;
align-items: center; justify-content: center;
justify-content: center; }
}
.title {
.title { font-family: PingFangSC-Medium;
font-family: PingFangSC-Medium; font-size: 18px;
font-size: 18px; color: #000000;
color: #000000; text-align: center;
text-align: center; line-height: 26px;
line-height: 26px; font-weight: 500;
font-weight: 500; }
}
.count-num { .count-num {
margin: 5% 0 5% 0; margin: 5% 0 5% 0;
align-items: flex-end; align-items: flex-end;
.num {
display: inline-block; .num {
font-family: AlibabaPuHuiTi_2_65_Medium; display: inline-block;
font-size: 50px; font-family: AlibabaPuHuiTi_2_65_Medium;
color: #3774f6; font-size: 50px;
line-height: 44px; color: #3774f6;
font-weight: 500; line-height: 44px;
} font-weight: 500;
} }
}
.operating-instructions {
margin-bottom: 8%; .operating-instructions {
.title { margin-bottom: 8%;
font-size: 13px;
color: #4a4a4a; .title {
line-height: 24px; font-size: 13px;
font-weight: 600; color: #4a4a4a;
text-align: left; line-height: 24px;
} font-weight: 600;
.instructions-item { text-align: left;
font-size: 12px; }
color: #7c7c7c;
line-height: 22px; .instructions-item {
font-weight: 400; font-size: 12px;
} color: #7c7c7c;
} line-height: 22px;
font-weight: 400;
// 打包按钮 }
.bottom-row { }
position: absolute;
bottom: 24px; // 打包按钮
left: 50%; .bottom-row {
transform: translateX(-50%); position: absolute;
bottom: 24px;
.button { left: 50%;
display: flex; transform: translateX(-50%);
align-items: center;
justify-content: center; .button {
background-image: linear-gradient(180deg, #3773f6 0%, #2c57f6 99%); display: flex;
box-shadow: 0px 10px 24px 0px rgba(51, 104, 246, 0.24); align-items: center;
border-radius: 27px; justify-content: center;
width: 160px; background-image: linear-gradient(180deg, #3773f6 0%, #2c57f6 99%);
height: 40px; box-shadow: 0px 10px 24px 0px rgba(51, 104, 246, 0.24);
color: #fff; border-radius: 27px;
} width: 160px;
} height: 40px;
color: #fff;
// 关闭按钮 }
.close-button { }
position: absolute;
bottom: -40px; // 关闭按钮
left: 50%; .close-button {
transform: translateX(-50%); position: absolute;
.iconfont { bottom: -40px;
font-size: 24px; left: 50%;
} transform: translateX(-50%);
}
} .iconfont {
} font-size: 24px;
</style> }
}
}
}
</style>
\ No newline at end of file
...@@ -471,25 +471,17 @@ export function getUserList() { ...@@ -471,25 +471,17 @@ export function getUserList() {
}); });
} }
export function zipCompress(name) { export function zipCompress(sourceDir, zipPath) {
const BASE_PATH = "_documents/data"; return new Promise((resolve, reject) => {
const targetPath = plus.io.convertLocalFileSystemURL(BASE_PATH + '/photos'); plus.zip.compress(
const zipfile = plus.io.convertLocalFileSystemURL(BASE_PATH + `/${name}.zip`); plus.io.convertLocalFileSystemURL(sourceDir),
plus.zip.compress( plus.io.convertLocalFileSystemURL(zipPath),
targetPath, {
zipfile, { recursive: true,
recursive: true, overwrite: true,
overwrite: true },
}, () => resolve(zipPath),
function() { (error) => reject(`压缩失败: ${error.message}`)
plus.io.resolveLocalFileSystemURL(zipfile, function(zipEntry) { );
zipEntry.file(function(file) { });
console.log("ZIP 文件大小: " + file.size + " 字节");
});
});
},
function(error) {
alert("压缩失败: " + error.message);
}
);
} }
\ No newline at end of file
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论