Index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. <template>
  2. <div class="hello">
  3. <link href="static/xspreadsheet/xspreadsheet.css" rel="stylesheet" />
  4. <div id="header"></div>
  5. <div class="edit-bar" v-if="item_info.ItemPermn">
  6. <el-button type="primary" size="mini" @click="save">{{$t('save')}}</el-button>
  7. <el-dropdown @command="dropdownCallback">
  8. <el-button size="mini">
  9. {{$t('more')}}
  10. <i class="el-icon-arrow-down el-icon--right"></i>
  11. </el-button>
  12. <el-dropdown-menu slot="dropdown">
  13. <el-dropdown-item :command="shareItem">{{$t('share')}}</el-dropdown-item>
  14. <router-link :to="'/item/setting/'+item_info.item_id" v-if="item_info.ItemCreator">
  15. <el-dropdown-item>{{$t('item_setting')}}</el-dropdown-item>
  16. </router-link>
  17. <el-dropdown-item :command="()=>{importDialogVisible = true}">{{$t('import_file')}}</el-dropdown-item>
  18. <el-dropdown-item :command="exportFile">{{$t('export')}}</el-dropdown-item>
  19. <el-dropdown-item :command="goback">{{$t('goback')}}</el-dropdown-item>
  20. </el-dropdown-menu>
  21. </el-dropdown>
  22. </div>
  23. <div class="edit-bar" v-if="!item_info.ItemPermn">
  24. <el-button size="mini" @click="goback">{{$t('goback')}}</el-button>
  25. </div>
  26. <div id="table-item"></div>
  27. <el-dialog
  28. :title="$t('share')"
  29. :visible.sync="dialogVisible"
  30. width="600px"
  31. :close-on-click-modal="false"
  32. class="text-center"
  33. >
  34. <p>
  35. {{$t('item_address')}} :
  36. <code>{{share_item_link}}</code>
  37. </p>
  38. <p>
  39. <a
  40. href="javascript:;"
  41. class="home-phone-butt"
  42. v-clipboard:copyhttplist="copyText"
  43. v-clipboard:success="onCopy"
  44. >{{$t('copy_link')}}</a>
  45. </p>
  46. <p style="border-bottom: 1px solid #eee;">
  47. <img id style="width:114px;height:114px;" :src="qr_item_link" />
  48. </p>
  49. <span slot="footer" class="dialog-footer">
  50. <el-button type="primary" @click="dialogVisible = false">{{$t('confirm')}}</el-button>
  51. </span>
  52. </el-dialog>
  53. <el-dialog
  54. :title="$t('import_excel')"
  55. :visible.sync="importDialogVisible"
  56. width="600px"
  57. :close-on-click-modal="false"
  58. class="text-center"
  59. >
  60. <p>
  61. <input
  62. type="file"
  63. name="xlfile"
  64. id="xlf"
  65. @change="(e)=>{
  66. improtFile(e.target.files)
  67. }"
  68. />
  69. </p>
  70. <span slot="footer" class="dialog-footer">
  71. <el-button type="primary" @click="importDialogVisible = false">{{$t('confirm')}}</el-button>
  72. </span>
  73. </el-dialog>
  74. </div>
  75. </template>
  76. <style scoped>
  77. .edit-bar {
  78. position: absolute;
  79. right: 10px;
  80. margin-top: 5px;
  81. }
  82. .edit-bar > button {
  83. margin-right: 10px;
  84. }
  85. </style>
  86. <script>
  87. if (typeof window !== 'undefined') {
  88. var $s = require('scriptjs')
  89. }
  90. export default {
  91. props: {
  92. item_info: ''
  93. },
  94. data() {
  95. return {
  96. menu: '',
  97. content: '',
  98. page_title: '',
  99. page_id: '',
  100. dialogVisible: false,
  101. share_item_link: '',
  102. qr_item_link: '',
  103. copyText: '',
  104. spreadsheetObj: {},
  105. spreadsheetData: {},
  106. isLock: 0,
  107. isEditable: 0,
  108. intervalId: 0,
  109. importDialogVisible: false
  110. }
  111. },
  112. components: {},
  113. methods: {
  114. getPageContent(page_id) {
  115. var that = this
  116. if (!page_id) {
  117. page_id = that.page_id
  118. }
  119. this.request('/api/page/info', {
  120. page_id: page_id
  121. }).then(response => {
  122. if (response.data.page_content) {
  123. let objData
  124. try {
  125. // 先定义一个html反转义的函数
  126. const unescapeHTML = str =>
  127. str.replace(
  128. /&amp;|&lt;|&gt;|&#39;|&quot;/g,
  129. tag =>
  130. ({
  131. '&amp;': '&',
  132. '&lt;': '<',
  133. '&gt;': '>',
  134. '&#39;': "'",
  135. '&quot;': '"'
  136. }[tag] || tag)
  137. )
  138. objData = JSON.parse(
  139. unescapeHTML(decodeURIComponent(response.data.page_content))
  140. )
  141. } catch (error) {
  142. objData = {}
  143. }
  144. this.spreadsheetData = objData
  145. // 初始化表格
  146. this.initSheet()
  147. if (this.item_info.ItemPermn) {
  148. this.draft()
  149. }
  150. }
  151. })
  152. },
  153. initSheet() {
  154. if (!x_spreadsheet) return false
  155. let mode = this.isEditable ? 'edit' : 'read'
  156. document.getElementById('table-item').innerHTML = '' // 清空原来的东西
  157. this.spreadsheetObj = null
  158. // 初始化表格
  159. this.spreadsheetObj = x_spreadsheet('#table-item', {
  160. mode: mode, // edit | read
  161. showToolbar: true,
  162. row: {
  163. len: 500,
  164. height: 25
  165. }
  166. }).loadData(this.spreadsheetData) // load data
  167. },
  168. shareItem() {
  169. let path = this.item_info.item_domain
  170. ? this.item_info.item_domain
  171. : this.item_info.item_id
  172. this.share_item_link = this.getRootPath() + '#/' + path
  173. this.qr_item_link =
  174. DocConfig.server +
  175. '/api/common/qrcode&size=3&url=' +
  176. encodeURIComponent(this.share_item_link)
  177. this.dialogVisible = true
  178. this.copyText =
  179. this.item_info.item_name + ' -- ShowDoc \r\n' + this.share_item_link
  180. },
  181. onCopy() {
  182. this.$message(this.$t('copy_success'))
  183. },
  184. save() {
  185. this.request('/api/page/save', {
  186. page_id: this.page_id,
  187. page_title: this.item_info.item_name,
  188. item_id: this.item_info.item_id,
  189. is_urlencode: 1,
  190. page_content: encodeURIComponent(
  191. JSON.stringify(this.spreadsheetObj.getData())
  192. )
  193. }).then(data => {
  194. // console.log(data)
  195. this.$message({
  196. showClose: true,
  197. message: '保存成功',
  198. type: 'success'
  199. })
  200. // 删除草稿
  201. this.deleteDraft()
  202. })
  203. },
  204. goback() {
  205. this.$router.push({
  206. path: '/item/index'
  207. })
  208. // 由于x_spreadsheet的固有缺陷,只能重新刷新销毁实例了
  209. setTimeout(() => {
  210. window.location.reload()
  211. }, 200)
  212. },
  213. dropdownCallback(data) {
  214. if (data) {
  215. data()
  216. }
  217. },
  218. // 草稿
  219. draft() {
  220. var that = this
  221. var pkey = 'page_content_' + this.page_id
  222. // 定时保存文本内容到localStorage
  223. setInterval(() => {
  224. var content = JSON.stringify(this.spreadsheetObj.getData())
  225. localStorage.setItem(pkey, content)
  226. }, 30 * 1000)
  227. // 检测是否有定时保存的内容
  228. var page_content = JSON.parse(localStorage.getItem(pkey))
  229. if (
  230. page_content &&
  231. page_content.length > 0 &&
  232. localStorage.getItem(pkey) !=
  233. JSON.stringify(this.spreadsheetObj.getData())
  234. ) {
  235. localStorage.removeItem(pkey)
  236. that
  237. .$confirm(that.$t('draft_tips'), '', {
  238. showClose: false
  239. })
  240. .then(() => {
  241. this.spreadsheetData = page_content
  242. // 初始化表格
  243. this.initSheet()
  244. localStorage.removeItem(pkey)
  245. })
  246. .catch(() => {
  247. localStorage.removeItem(pkey)
  248. })
  249. }
  250. },
  251. // 遍历删除草稿
  252. deleteDraft() {
  253. for (var i = 0; i < localStorage.length; i++) {
  254. var name = localStorage.key(i)
  255. if (name.indexOf('page_content_') > -1) {
  256. localStorage.removeItem(name)
  257. }
  258. }
  259. },
  260. // 锁定
  261. setLock() {
  262. if (this.page_id > 0) {
  263. this.request('/api/page/setLock', {
  264. page_id: this.page_id,
  265. item_id: this.item_info.item_id
  266. }).then(() => {
  267. this.isLock = 1
  268. })
  269. }
  270. },
  271. // 解除锁定
  272. unlock() {
  273. if (!this.isLock) {
  274. return // 本来处于未锁定中的话,不发起请求
  275. }
  276. this.request('/api/page/setLock', {
  277. page_id: this.page_id,
  278. item_id: this.item_info.item_id,
  279. lock_to: 1000
  280. }).then(() => {
  281. this.isLock = 0
  282. })
  283. },
  284. // 如果用户处于锁定状态的话,用心跳保持锁定
  285. heartBeatLock() {
  286. this.intervalId = setInterval(() => {
  287. if (this.isLock) {
  288. this.setLock()
  289. }
  290. }, 3 * 60 * 1000)
  291. },
  292. // 判断页面是否被锁定编辑
  293. remoteIsLock() {
  294. this.request('/api/page/isLock', {
  295. page_id: this.page_id
  296. }).then(res => {
  297. // 判断已经锁定了不
  298. if (res.data.lock > 0) {
  299. if (res.data.is_cur_user > 0) {
  300. this.isLock = 1
  301. this.isEditable = 1
  302. this.initSheet()
  303. this.heartBeatLock()
  304. } else {
  305. this.$alert(this.$t('locking') + res.data.lock_username)
  306. this.item_info.ItemPermn = false
  307. clearInterval(this.intervalId)
  308. this.deleteDraft()
  309. }
  310. } else {
  311. this.setLock() // 如果没有被别人锁定,则进编辑页面后自己锁定。
  312. this.isEditable = 1
  313. this.initSheet()
  314. this.heartBeatLock()
  315. }
  316. })
  317. },
  318. exportFile() {
  319. // 先定义一个函数
  320. const xtos = sdata => {
  321. var out = XLSX.utils.book_new()
  322. sdata.forEach(function(xws) {
  323. var aoa = [[]]
  324. var rowobj = xws.rows
  325. for (var ri = 0; ri < rowobj.len; ++ri) {
  326. var row = rowobj[ri]
  327. if (!row) continue
  328. aoa[ri] = []
  329. Object.keys(row.cells).forEach(function(k) {
  330. var idx = +k
  331. if (isNaN(idx)) return
  332. aoa[ri][idx] = row.cells[k].text
  333. })
  334. }
  335. var ws = XLSX.utils.aoa_to_sheet(aoa)
  336. XLSX.utils.book_append_sheet(out, ws, xws.name)
  337. })
  338. return out
  339. }
  340. /* build workbook from the grid data */
  341. var new_wb = xtos(this.spreadsheetObj.getData())
  342. /* generate download */
  343. XLSX.writeFile(new_wb, 'showdoc.xlsx')
  344. },
  345. improtFile(files) {
  346. const f = files[0]
  347. const stox = wb => {
  348. var out = []
  349. wb.SheetNames.forEach(function(name) {
  350. var o = { name: name, rows: {} }
  351. var ws = wb.Sheets[name]
  352. var aoa = XLSX.utils.sheet_to_json(ws, { raw: false, header: 1 })
  353. aoa.forEach(function(r, i) {
  354. var cells = {}
  355. r.forEach(function(c, j) {
  356. cells[j] = { text: c }
  357. })
  358. o.rows[i] = { cells: cells }
  359. })
  360. out.push(o)
  361. })
  362. return out
  363. }
  364. var reader = new FileReader()
  365. reader.onload = e => {
  366. var data = e.target.result
  367. var mdata = stox(XLSX.read(data, { type: 'array' }))
  368. if (mdata) {
  369. /* update x-spreadsheet */
  370. this.spreadsheetObj.loadData(mdata)
  371. this.importDialogVisible = false
  372. }
  373. }
  374. reader.readAsArrayBuffer(f)
  375. }
  376. },
  377. mounted() {
  378. this.menu = this.item_info.menu
  379. this.page_id = this.menu.pages[0].page_id
  380. // 加载依赖""
  381. $s([`static/xspreadsheet/xspreadsheet.js`], () => {
  382. $s(
  383. [
  384. `static/xspreadsheet/locale/zh-cn.js`,
  385. `static/xspreadsheet/locale/en.js`
  386. ],
  387. () => {
  388. if (DocConfig.lang == 'en') {
  389. x_spreadsheet.locale('en')
  390. } else {
  391. x_spreadsheet.locale('zh-cn')
  392. }
  393. this.getPageContent()
  394. if (this.item_info.ItemPermn) {
  395. this.remoteIsLock()
  396. }
  397. }
  398. )
  399. $s([`static/xspreadsheet/xlsx.full.min.js`])
  400. })
  401. },
  402. beforeDestroy() {
  403. this.$message.closeAll()
  404. clearInterval(this.intervalId)
  405. this.unlock()
  406. }
  407. }
  408. </script>