fIleOperation.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. <template>
  2. <el-dropdown trigger="click">
  3. <div class="el-dropdown-link" :title="$t('message.file')">
  4. <el-tooltip :content="$t('message.file')">
  5. <div>
  6. <img :src="local_storage" alt="" class="header_right_icon left">
  7. <span class="header_right_title_span">{{ $t('message.file') }}</span>
  8. </div>
  9. </el-tooltip>
  10. </div>
  11. <template #dropdown>
  12. <el-dropdown-menu>
  13. <el-dropdown-item class="header_menu_li" @click="cloudSave()" style="color: #26a69a;">{{
  14. $t('message.cloud_save') }}</el-dropdown-item>
  15. <el-dropdown-item class="header_menu_li" @click="importFile()" style="color: #26a69a;">{{
  16. $t('message.import') }}</el-dropdown-item>
  17. <el-dropdown-item class="header_menu_li" @click="exportFile()" style="color: #26a69a;">{{
  18. $t('message.Export') }}</el-dropdown-item>
  19. </el-dropdown-menu>
  20. </template>
  21. </el-dropdown>
  22. <el-dialog v-model="cloudDialog" :title="$t('message.cloud_save')" width="800">
  23. <el-row :gutter="24" style="text-align: center;">
  24. <el-col :span="7" style="margin-top: 15px;">
  25. <el-card :body-style="{ padding: '0px' }">
  26. <div style="padding: 14px;">
  27. <el-icon :size="80" @click="createFile()">
  28. <DocumentAdd />
  29. </el-icon>
  30. </div>
  31. </el-card>
  32. </el-col>
  33. <el-col :span="7" v-for="(item, index) in cloudData" style="margin-top: 15px;">
  34. <el-card :body-style="{ padding: '0px' }" @click="openFile($event, item)">
  35. <div class="cloud_card_top">
  36. <img @click="share($event, item.filenameid)" src="../../assets/img/分享.png" alt="">
  37. <div>
  38. <img @click="saveFile($event, item.filenameid)" src="../../assets//img/save.png" alt="">
  39. <img @click="deleteFile($event, item.filename)" src="../../assets//img/delete.png" alt="">
  40. </div>
  41. </div>
  42. <div class="cloud_card_bottom" style="padding: 14px;">
  43. <span>{{ item.filename }}</span>
  44. <el-icon :size="20">
  45. <Edit @click="update($event, item)" />
  46. </el-icon><br />
  47. <span>{{ $t('message.last_version') }}{{ new Date(item.date).toLocaleDateString() }}</span>
  48. </div>
  49. </el-card>
  50. </el-col>
  51. </el-row>
  52. <template #footer>
  53. <div class="dialog-footer">
  54. <el-button @click="cloudDialog = false">{{ $t('message.close') }}</el-button>
  55. </div>
  56. </template>
  57. </el-dialog>
  58. <el-dialog v-model="updateDialog" :title="updateTitle" width="400">
  59. <el-input v-model="newFilename" style="margin-top: 20px;"></el-input>
  60. <template #footer>
  61. <div class="dialog-footer">
  62. <el-button @click="sumbit" type="primary">{{ $t('message.ok') }}</el-button>
  63. <el-button @click="updateDialog = false">{{ $t('message.close') }}</el-button>
  64. </div>
  65. </template>
  66. </el-dialog>
  67. </template>
  68. <script setup>
  69. import { ref } from 'vue'
  70. import { ElMessage } from 'element-plus'
  71. import { Edit, DocumentAdd } from '@element-plus/icons-vue'
  72. import Blockly from 'blockly';
  73. import local_storage from '../../assets/img/local_storage.png'
  74. import '../../assets/css/header.css'
  75. import store from '../../stores/blockly'
  76. import { exportFileGlobal } from '../../gloabl/globalMethods'
  77. import userInfo from '../../stores/modules/userInfo'
  78. import axios from 'axios'
  79. import server from '@/config/index'
  80. import { useI18n } from 'vue-i18n'
  81. const { t } = useI18n() // 解构出t方法
  82. const props = defineProps({
  83. exportImportFileName: {
  84. type: String
  85. }
  86. })
  87. const emit = defineEmits(["setFileName"])
  88. const user = userInfo()
  89. const exportXml = store.useyXmlStore()
  90. const importFileName = store.useImportFileNameStore()
  91. const LoadBlocklyStatus = store.useLoadBlocklyStatus()
  92. const cloudDialog = ref(false)
  93. const cloudData = ref([])
  94. const updateDialog = ref(false)
  95. const updateData = ref({})
  96. const newFilename = ref('')
  97. const updateTitle = ref(t('mesage.add'))
  98. const cloudSave = () => {
  99. if (JSON.stringify(user.userInfo) != "{}") {
  100. axios.defaults.withCredentials = true
  101. axios.changeOrigin = true
  102. axios.get(`${server.host}blockx/files`).then(res => {
  103. // console.table(res.data)
  104. cloudData.value = res.data
  105. })
  106. cloudDialog.value = true
  107. } else {
  108. ElMessage({
  109. message: t('message.please_login'),
  110. type: 'warning',
  111. })
  112. }
  113. }
  114. // 导入文件
  115. const importFile = () => {
  116. const input = document.createElement('input');
  117. input.type = 'file';
  118. input.accept = '.xml';
  119. // input.onClick = e => {
  120. // e.stopPropagation();
  121. // }
  122. input.onchange = e => {
  123. const file = e.target.files[0];
  124. const reader = new FileReader();
  125. reader.onload = e => {
  126. let xml = e.target.result;
  127. exportXml.$patch({ xmlStr: xml })
  128. LoadBlocklyStatus.$patch({ status: true })
  129. importFileName.$patch({ fileName: file.name })
  130. emit("setFileName", file.name)
  131. localStorage.setItem('workspaceXml', xml)
  132. setTimeout(() => {
  133. LoadBlocklyStatus.$patch({ status: false })
  134. }, 100)
  135. };
  136. reader.readAsText(file);
  137. };
  138. input.click();
  139. }
  140. // 导出文件
  141. const exportFile = () => {
  142. let name = props.exportImportFileName + '.xml'
  143. if (exportXml.xmlStr && exportXml.xmlStr != `<xml xmlns="https://developers.google.com/blockly/xml"></xml>`) {
  144. exportFileGlobal(exportXml.xmlStr, name)
  145. } else {
  146. ElMessage({
  147. message: t('message.export_content_empty'),
  148. type: 'warning',
  149. })
  150. }
  151. // let blob = new Blob([exportXml], { type: 'text/plain;charset=utf-8' })
  152. // // 创建一个指向Blob对象的URL
  153. // const textURL = window.URL.createObjectURL(blob);
  154. // // 创建一个临时的a标签用于触发下载
  155. // const link = document.createElement('a');
  156. // link.href = textURL;
  157. // link.download = props.exportImportFileName + '.xml'; // 指定下载文件的名称
  158. // document.body.appendChild(link); // 将a标签添加到文档中
  159. // // 触发点击事件以开始下载
  160. // link.click();
  161. // // 清理:移除a标签,释放创建的URL
  162. // document.body.removeChild(link);
  163. // window.URL.revokeObjectURL(textURL);
  164. }
  165. // 分享
  166. const share = (e, id) => {
  167. e.stopPropagation();
  168. // console.log("分享", location.href)
  169. // "https://beta.v.cocorobo.cn/?lang=zh-hans&Qd=5ed71aeb0ff4fc69b32e866b_1721619372665";
  170. const text = location.href + "&Qd=" + id;
  171. navigator.clipboard.writeText(text).then(() => {
  172. // console.log('内容已复制到剪贴板');
  173. ElMessage({
  174. message: t('message.link_copy'),
  175. type: 'success',
  176. })
  177. }).catch(err => {
  178. console.error('复制到剪贴板失败: ', err);
  179. ElMessage({
  180. message: t('message.link_copy_error'),
  181. type: 'warning',
  182. })
  183. });
  184. }
  185. // 删除文件
  186. const deleteFile = (e, id) => {
  187. e.stopPropagation();
  188. // https://api.cocorobo.cn/blockx/1721619372665
  189. axios.delete(`https://api.cocorobo.cn/blockx/${id}`).then(res => {
  190. console.log(res)
  191. if (res.status == 200) {
  192. ElMessage({
  193. message: t('message.delete_success'),
  194. type: 'success',
  195. })
  196. cloudSave()
  197. } else {
  198. ElMessage({
  199. message: t('message.delete_error'),
  200. type: 'error',
  201. })
  202. }
  203. })
  204. }
  205. // 保存文件
  206. const saveFile = (e, name) => {
  207. e.stopPropagation();
  208. // https://api.cocorobo.cn/blockx/1716428157190
  209. let form = new FormData()
  210. // form.append('platformType', "CocoPi")
  211. form.append('filename', name)
  212. form.append('xml', exportXml.xmlStr)
  213. // form.append('code', "")
  214. axios.put(`https://api.cocorobo.cn/blockx/${name}`, form).then(res => {
  215. console.log(res)
  216. if (res.data == "OK") {
  217. ElMessage({
  218. message: t('message.save_success'),
  219. type: 'success',
  220. })
  221. }
  222. else {
  223. ElMessage({
  224. message: t('message.save_error'),
  225. type: 'error',
  226. })
  227. }
  228. })
  229. }
  230. // 打开文件
  231. const openFile = (e, item) => {
  232. e.stopPropagation();
  233. // https://api.cocorobo.cn/blockx/5ed71aeb0ff4fc69b32e866b_1710676497206
  234. axios.get(`https://api.cocorobo.cn/blockx/${item.filenameid}`).then(res => {
  235. console.log(res.data.xml)
  236. if (res.status == 200) {
  237. let xml = res.data.xml
  238. if(xml.indexOf('$') == 4){
  239. xml = xml.slice(5)
  240. }
  241. importFileName.$patch({ fileName: item.filename })
  242. emit("setFileName", item.filename)
  243. exportXml.$patch({ xmlStr: xml })
  244. LoadBlocklyStatus.$patch({ status: true })
  245. localStorage.setItem('workspaceXml', xml)
  246. setTimeout(() => {
  247. LoadBlocklyStatus.$patch({ status: false })
  248. cloudDialog.value = false
  249. }, 100)
  250. }
  251. else {
  252. ElMessage({
  253. message: t('message.open_error'),
  254. type: 'error',
  255. })
  256. }
  257. })
  258. }
  259. //
  260. const update = (e, item) => {
  261. updateDialog.value = true
  262. e.stopPropagation();
  263. console.log(item)
  264. updateData.value = item
  265. newFilename.value = item.filename
  266. updateTitle.value = t('message.update')
  267. }
  268. const sumbit = () => {
  269. if (updateTitle.value == t('message.add')) {
  270. let params = {
  271. filename: newFilename.value,
  272. type: '',
  273. xml: exportXml.xmlStr
  274. }
  275. axios.post("https://api.cocorobo.cn/blockx/", params).then(res => {
  276. console.log(res)
  277. if (res.statusText == "OK") {
  278. cloudSave()
  279. ElMessage({
  280. message: t('message.add_success'),
  281. type: 'success',
  282. })
  283. updateDialog.value = false
  284. } else {
  285. ElMessage({
  286. message: t('message.add_error'),
  287. type: 'error',
  288. })
  289. }
  290. })
  291. } else {
  292. let data = {
  293. currentFilename: updateData.value.filename,
  294. newFilename: newFilename.value
  295. }
  296. axios.put('https://api.cocorobo.cn/blockx/modify', data).then(res => {
  297. console.log(res)
  298. if (res.data == "Filename update.") {
  299. cloudSave()
  300. ElMessage({
  301. message: t('message.update_success'),
  302. type: 'success',
  303. })
  304. updateDialog.value = false
  305. } else {
  306. ElMessage({
  307. message: t('message.update_error'),
  308. type: 'error',
  309. })
  310. }
  311. })
  312. }
  313. }
  314. const createFile = () => {
  315. updateDialog.value = true
  316. newFilename.value = ""
  317. updateTitle.value = t('message.add')
  318. }
  319. </script>
  320. <style scoped lang="scss">
  321. .cloud_card_top {
  322. display: flex;
  323. justify-content: space-between;
  324. background-color: #454fb5;
  325. padding: 8px 10px;
  326. img {
  327. width: 25px;
  328. height: 25px;
  329. }
  330. }
  331. </style>