index.vue 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  1. <template>
  2. <div class="page">
  3. <el-card shadow="nover">
  4. <h2>远程配置</h2>
  5. <div class="container">
  6. <div class="selectContainer">
  7. <article style="margin-right: 20px">
  8. <span class="label">产品:</span>
  9. <el-select v-model="product" filterable @change="handleChange">
  10. <el-option v-for="item in productOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
  11. </el-select>
  12. </article>
  13. <article>
  14. <span class="label">配置格式:</span>
  15. <el-select v-model="currentConfig.configFormat" filterable>
  16. <el-option v-for="item in configTypeOptions" :key="item.key" :label="item.key" :value="item.key"> </el-option>
  17. </el-select>
  18. </article>
  19. </div>
  20. <div class="editorTitle">
  21. <div class="leftLabel">
  22. 配置模板
  23. <span style="color: #999; font-size: 12px; font-weight: normal; display: inline-block; margin-left: 10px; margin-right: 50px">当前文件大小{{ size }}KB(上限64KB)</span>
  24. <span style="color: #999; font-size: 14px; font-weight: normal" v-if="currentConfig.gmtCreate">提交于{{ currentConfig.gmtCreate }}</span>
  25. </div>
  26. <div class="rightLabel">
  27. <el-icon size="18" class="icon">
  28. <question-filled />
  29. </el-icon>
  30. <span v-if="onlineOpen">远程配置已经打开</span>
  31. <span v-if="!onlineOpen">远程配置已经关闭</span>
  32. <el-switch v-model="onlineOpen" active-color="#13ce66" inactive-color="#ddd"></el-switch>
  33. </div>
  34. </div>
  35. <!-- <div style="border: 1px solid #ccc; border-top: none">-->
  36. <div style="border-top: none">
  37. <codeEditor
  38. class="params flex1"
  39. ref="mirrorRef"
  40. mode=""
  41. :onchange="onchange"
  42. :readOnly="true"
  43. :content="currentConfig.configContent"
  44. :cursorBlinkRate="-1"></codeEditor>
  45. </div>
  46. <div class="btnContainer">
  47. <div v-if="!onlineOpen">
  48. <el-tooltip class="box-item" effect="dark" placement="top" content="请先开启远程配置">
  49. <div style="margin-right: 20px"><el-button type="primary" disabled>编辑</el-button></div>
  50. </el-tooltip>
  51. </div>
  52. <div v-else style="margin-right: 20px"><el-button type="primary" @click="edit">编辑</el-button></div>
  53. <div v-if="canSave"><el-button type="primary" @click="saveDialogVisible = true">保存</el-button></div>
  54. <div v-else><el-button type="primary" @click="updateDialogVisible = true">批量更新</el-button></div>
  55. </div>
  56. <h2>配置版本记录</h2>
  57. <el-table :data="tableData.data"
  58. style="width: 100%"
  59. row-key="id"
  60. default-expand-all
  61. :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
  62. v-loading="tableData.loading">
  63. <el-table-column prop="configNumber" label="编号" width="200"> </el-table-column>
  64. <el-table-column prop="gmtCreate" label="版本更新时间"></el-table-column>
  65. <el-table-column label="操作" width="200">
  66. <template #default="scope">
  67. <el-button size="small" type="text" @click="look(scope.row, scope.$index)">查看</el-button>
  68. </template>
  69. </el-table-column>
  70. </el-table>
  71. </div>
  72. </el-card>
  73. <el-dialog v-model="updateDialogVisible" width="40%">
  74. <div class="saveDialogContainer">
  75. <div class="icon">
  76. <el-icon color="#E6A23C" size="26">
  77. <warning-filled />
  78. </el-icon>
  79. </div>
  80. <div class="string">
  81. <h2>是否确认对该产品下的所有设备进行批量远程配置更新</h2>
  82. <p>注:该产品下的所有设备将自动更新该配置文件.设备端需订阅远程配置的topic</p>
  83. <p>指定产品:{{ selectProduct }}</p>
  84. <p>设备范围:所有设备</p>
  85. </div>
  86. </div>
  87. <template #footer>
  88. <span class="dialog-footer">
  89. <el-button @click="updateDialogHandle" type="primary">确认更新</el-button>
  90. <el-button @click="updateDialogVisible = false">取消</el-button>
  91. </span>
  92. </template>
  93. </el-dialog>
  94. <el-dialog v-model="saveDialogVisible" width="40%">
  95. <div class="saveDialogContainer">
  96. <div class="icon">
  97. <el-icon color="#E6A23C" size="26">
  98. <warning-filled />
  99. </el-icon>
  100. </div>
  101. <div class="string">
  102. <h3>是否保存当前配置信息? 保存后可以手动将配置批量更新到该产品下的所有设备,设备也可以主动获取配置</h3>
  103. </div>
  104. </div>
  105. <template #footer>
  106. <span class="dialog-footer">
  107. <el-button @click="saveHandle" type="primary">确认</el-button>
  108. <el-button @click="saveDialogVisible = false">取消</el-button>
  109. </span>
  110. </template>
  111. </el-dialog>
  112. <el-dialog v-model="lookDialogVisible" width="40%">
  113. <div class="saveDialogContainer" v-if="lookDialogVisible">
  114. <codeEditor class="params flex1"
  115. ref="mirrorRefDialog"
  116. mode=""
  117. :onchange="onchange"
  118. :readOnly="true"
  119. :content="tableData.data[lookIndex!].content"
  120. :cursorBlinkRate="-1"></codeEditor>
  121. </div>
  122. <template #footer>
  123. <span class="dialog-footer">
  124. <el-button @click="lookSureDialogVisible = true" type="primary">恢复至此版本</el-button>
  125. <el-button @click="lookDialogVisible = false">取消</el-button>
  126. </span>
  127. </template>
  128. </el-dialog>
  129. <el-dialog v-model="lookSureDialogVisible" width="40%">
  130. <div class="saveDialogContainer">
  131. <div class="icon">
  132. <el-icon color="#E6A23C" size="26">
  133. <warning-filled />
  134. </el-icon>
  135. </div>
  136. <div class="string">
  137. <h3>恢复版本</h3>
  138. <p>是否确认恢复到{{ tableData.data[lookIndex!].time }}的版本</p>
  139. </div>
  140. </div>
  141. <template #footer>
  142. <span class="dialog-footer">
  143. <el-button @click="sureRestore" type="primary">确认</el-button>
  144. <el-button @click="lookSureDialogVisible = false">取消</el-button>
  145. </span>
  146. </template>
  147. </el-dialog>
  148. </div>
  149. </template>
  150. <script lang="ts">
  151. import { reactive, onMounted, ref, defineComponent, watch, nextTick } from 'vue'
  152. import { ElMessage } from 'element-plus'
  153. import { QuestionFilled, WarningFilled } from '@element-plus/icons-vue'
  154. import api from '/@/api/system'
  155. import codeEditor from '/@/components/codeEditor/index.vue'
  156. export default defineComponent({
  157. name: 'online',
  158. components: { QuestionFilled, WarningFilled, codeEditor },
  159. setup() {
  160. const product = ref()
  161. const productOptions = ref<{ value: string; label: string; status: string }[]>([])
  162. const canSave = ref(false)
  163. const onlineOpen = ref(false)
  164. const mirrorRef = ref<any>() // 编辑器
  165. const mirrorRefDialog = ref<any>()
  166. const configTypeOptions = [{ key: 'json' }, { key: 'yaml' }]
  167. const selectProduct = ref(); // 选中产品名称
  168. const currentConfig = ref({
  169. configContent: '',
  170. configFormat: 'json',
  171. configNumber: '',
  172. gmtCreate: '',
  173. id: '',
  174. scope: '',
  175. })
  176. // 页面加载时
  177. onMounted(() => {
  178. api.remoteconf.getProductList({ status: '1', name: '' }).then((res: any) => {
  179. if (res.product) {
  180. productOptions.value = res.product.map((item: any) => {
  181. return {
  182. value: item.key,
  183. label: item.name,
  184. status: item.status,
  185. }
  186. })
  187. if (productOptions.value.length > 0) {
  188. product.value = productOptions.value[0].value
  189. selectProduct.value = productOptions.value[0].label
  190. }
  191. }
  192. })
  193. })
  194. const edit = () => {
  195. mirrorRef.value.setOption('readOnly', false)
  196. // 显示光标
  197. mirrorRef.value.setOption('cursorBlinkRate', 530);
  198. canSave.value = true
  199. }
  200. const saveDialogVisible = ref(false)
  201. const updateDialogVisible = ref(false)
  202. watch(
  203. () => onlineOpen.value,
  204. () => {
  205. //初始化 然后由false变为 true 不用管
  206. // 这里是 由false变为true
  207. if (!onlineOpen.value) {
  208. canSave.value = false
  209. mirrorRef.value.setOption('readOnly', true)
  210. // 隐藏光标
  211. mirrorRef.value.setOption('cursorBlinkRate', -1);
  212. }
  213. }
  214. )
  215. watch(() => product.value, () => {
  216. //product.value 就是那个key options里面转化了
  217. currentConfig.value.gmtCreate = '';
  218. mirrorRef.value.setValue('');
  219. tableData.data = [];
  220. updateTableData()
  221. })
  222. // 选择下拉框替换批量更新产品名称
  223. const handleChange = (value) => {
  224. selectProduct.value = productOptions.value.find((item) => item.value === value).label;
  225. }
  226. const updateTableData = () => {
  227. api.remoteconf.queryThingConfig({ pageNum: 1, PageSize: 50, productKey: product.value })
  228. .then((res: any) => {
  229. if (res.remoteconf) {
  230. // tableData.data = res.remoteconf.slice(1)
  231. tableData.data = res.remoteconf
  232. // 重塑编号
  233. for (let i = 0; i < tableData.data.length; i++) {
  234. if ((i + 1) < 10) {
  235. tableData.data[i].configNumber = '0' + (i + 1);
  236. } else {
  237. tableData.data[i].configNumber = i + 1;
  238. }
  239. }
  240. currentConfig.value = res.remoteconf[0]
  241. mirrorRef.value.setValue(currentConfig.value.configContent)
  242. }
  243. }).catch((error: any) => {
  244. ElMessage.error(error)
  245. })
  246. }
  247. const size = ref('0')
  248. const saveHandle = () => {
  249. saveDialogVisible.value = false
  250. // let now = new Date()
  251. // let year: number | string = now.getFullYear()
  252. // let month: number | string = now.getMonth() + 1 // getMonth() 返回的月份从 0 开始,所以需要 +1
  253. // let day: number | string = now.getDate()
  254. // let hour: number | string = now.getHours()
  255. // let minute: number | string = now.getMinutes()
  256. // let second: number | string = now.getSeconds()
  257. // // 如果月、日、小时、分钟或秒的值小于 10,前面补 0
  258. // if (month < 10) month = '0' + month
  259. // if (day < 10) day = '0' + day
  260. // if (hour < 10) hour = '0' + hour
  261. // if (minute < 10) minute = '0' + minute
  262. // if (second < 10) second = '0' + second
  263. // updateTime.value = year + '/' + month + '/' + day + ' ' + hour + ':' + minute + ':' + second
  264. api.remoteconf.saveThisConfig({
  265. scope: 'product',
  266. configName: '',
  267. configFormat: currentConfig.value.configFormat,
  268. configContent: mirrorRef.value.getValue(),
  269. configSize: size.value,
  270. status: productOptions.value.find((item: any) => item.value == product.value)?.status!,
  271. productKey: product.value,
  272. }).then((res: any) => {
  273. ElMessage.success('保存成功')
  274. onlineOpen.value = false
  275. updateTableData()
  276. }).catch((err: any) => {
  277. ElMessage.error(err)
  278. })
  279. }
  280. const onchange = (e: any, b: any) => {
  281. var blob = new Blob([e.getValue()], { type: 'text/plain' })
  282. size.value = (blob.size / 1024).toFixed(2)
  283. }
  284. const tableData = reactive({
  285. data: <any[]>[],
  286. loading: false,
  287. })
  288. const lookIndex = ref<null | number>(0)
  289. const lookDialogVisible = ref(false)
  290. const lookSureDialogVisible = ref(false)
  291. const look = (item: any, index: number) => {
  292. lookDialogVisible.value = true
  293. lookIndex.value = index
  294. setTimeout(() => {
  295. mirrorRefDialog.value.setValue(tableData.data[index].configContent)
  296. }, 4)
  297. }
  298. const sureRestore = () => {
  299. lookDialogVisible.value = false
  300. lookSureDialogVisible.value = false
  301. currentConfig.value.configFormat = tableData.data[lookIndex.value!].configFormat
  302. mirrorRef.value.setValue(tableData.data[lookIndex.value!].configContent)
  303. nextTick(() => {
  304. saveHandle()
  305. })
  306. }
  307. const updateDialogHandle = () => {
  308. updateDialogVisible.value = false
  309. saveHandle()
  310. }
  311. return {
  312. selectProduct,
  313. productOptions,
  314. product,
  315. canSave,
  316. edit,
  317. onlineOpen,
  318. updateDialogVisible,
  319. saveDialogVisible,
  320. mirrorRef,
  321. size,
  322. saveHandle,
  323. onchange,
  324. tableData,
  325. look,
  326. lookIndex,
  327. lookDialogVisible,
  328. lookSureDialogVisible,
  329. sureRestore,
  330. mirrorRefDialog,
  331. configTypeOptions,
  332. updateDialogHandle,
  333. currentConfig,
  334. handleChange,
  335. }
  336. },
  337. })
  338. </script>
  339. <style scoped lang="scss">
  340. .selectContainer {
  341. display: flex;
  342. }
  343. .label {
  344. display: inline-block;
  345. padding-right: 12px;
  346. }
  347. h2 {
  348. margin-bottom: 10px;
  349. }
  350. article {
  351. margin-bottom: 10px;
  352. }
  353. .container :deep(.ql-toolbar.ql-snow) {
  354. display: none;
  355. }
  356. .editorTitle {
  357. width: 100%;
  358. //border: 1px solid #ccc;
  359. padding: 10px;
  360. height: 50px
  361. //background-color: #eee;
  362. }
  363. .leftLabel {
  364. float: left;
  365. height: 32px;
  366. line-height: 32px;
  367. font-weight: 600;
  368. }
  369. .rightLabel {
  370. float: right;
  371. span {
  372. display: inline-block;
  373. margin: 0 10px;
  374. }
  375. .icon {
  376. float: left;
  377. margin-top: 6px;
  378. }
  379. }
  380. .btnContainer {
  381. width: 200px;
  382. margin: 10px auto;
  383. display: flex;
  384. }
  385. .saveDialogContainer {
  386. display: flex;
  387. h2 {
  388. color: #333;
  389. }
  390. p {
  391. color: #999;
  392. margin: 8px 0;
  393. }
  394. .icon {
  395. margin: 2px 10px;
  396. }
  397. }
  398. </style>