index.vue 13 KB

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