index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. <template>
  2. <el-card>
  3. <template #header>
  4. <div
  5. v-if="useUserStoreHook().existsRole(['eng', 'root'])"
  6. class="card-header"
  7. >
  8. <span style="margin-right: 10px">
  9. 启用编辑
  10. <el-switch
  11. v-model="swEditable"
  12. class="ml-2"
  13. inline-prompt
  14. style="--el-switch-off-color: #666666"
  15. />
  16. </span>
  17. <el-button @click="handleExport"> 导出到Excel </el-button>
  18. <el-button
  19. v-if="swEditable"
  20. type="primary"
  21. @click="dlgImportExcelVisible = true"
  22. >
  23. 从Excel导入
  24. </el-button>
  25. <el-button v-if="swEditable" type="primary" @click="updateAnchors">
  26. 更新数据库
  27. </el-button>
  28. <el-button @click="reloadAnchors"> 重置列表 </el-button>
  29. <el-button
  30. v-if="swEditable"
  31. :icon="useRenderIcon(AddFill)"
  32. @click="onAdd"
  33. >
  34. 添加一行数据
  35. </el-button>
  36. </div>
  37. </template>
  38. <div>
  39. <pure-table
  40. align-whole="center"
  41. :header-cell-style="{
  42. background: 'var(--el-fill-color-light)',
  43. color: 'var(--el-text-color-primary)'
  44. }"
  45. :columns="columns"
  46. :table-key="tableKey"
  47. border
  48. adaptive
  49. :adaptiveConfig="adaptiveConfig"
  50. showOverflowTooltip
  51. :loading="false"
  52. :loading-config="loadingConfig"
  53. :data="
  54. dataList.slice(
  55. (pagination.currentPage - 1) * pagination.pageSize,
  56. pagination.currentPage * pagination.pageSize
  57. )
  58. "
  59. :pagination="pagination"
  60. @page-size-change="onSizeChange"
  61. @page-current-change="onCurrentChange"
  62. >
  63. <template #toolbar>
  64. <el-button plain size="small">查看日志</el-button>
  65. <el-button plain size="small">导出数据</el-button>
  66. <el-button type="primary" size="small">添加一行数据</el-button>
  67. </template>
  68. <template #operation="{ row, index }">
  69. <div v-if="swEditable && !editMap[index]?.editable">
  70. <el-button
  71. class="reset-margin"
  72. link
  73. type="primary"
  74. :icon="useRenderIcon(Delete)"
  75. @click="onDel(row)"
  76. >
  77. 删除
  78. </el-button>
  79. <el-button
  80. class="reset-margin"
  81. link
  82. type="primary"
  83. @click="onEdit(row, index)"
  84. :icon="useRenderIcon(EditFill)"
  85. >
  86. 修改
  87. </el-button>
  88. </div>
  89. <div v-if="swEditable && editMap[index]?.editable">
  90. <el-button
  91. class="reset-margin"
  92. link
  93. type="primary"
  94. @click="onSave(index)"
  95. :icon="useRenderIcon(SaveFill)"
  96. >
  97. 保存
  98. </el-button>
  99. <el-button
  100. class="reset-margin"
  101. link
  102. @click="onCancel(index)"
  103. :icon="useRenderIcon(Close)"
  104. >
  105. 取消
  106. </el-button>
  107. </div>
  108. </template>
  109. </pure-table>
  110. </div>
  111. <el-dialog
  112. v-model="dlgImportExcelVisible"
  113. title="Excel导入"
  114. width="60%"
  115. :before-close="handleDlgClose"
  116. destroy-on-close
  117. top="6vh"
  118. >
  119. <importExcel ref="importExcelRef" @onExcelData="onExcelData" />
  120. <template #footer>
  121. <span class="dialog-footer">
  122. <el-button @click="dlgImportExcelVisible = false">取消</el-button>
  123. <el-button type="primary" @click="handleDlgConfirm"> 导入 </el-button>
  124. </span>
  125. </template>
  126. </el-dialog>
  127. </el-card>
  128. </template>
  129. <script setup lang="ts">
  130. import { ref, onMounted } from "vue";
  131. import { useColumns } from "./columns";
  132. import Empty from "../empty.svg?component";
  133. import { useRenderIcon } from "@/components/ReIcon/src/hooks";
  134. import AddFill from "~icons/ep/plus";
  135. import Delete from "~icons/ep/delete";
  136. import EditFill from "~icons/ep/edit";
  137. import SaveFill from "~icons/bi/save";
  138. import Close from "~icons/ep/close";
  139. import { ElNotification, ElMessageBox } from "element-plus";
  140. import * as XLSX from "xlsx";
  141. import importExcel from "./importExcel.vue";
  142. import { useUserStoreHook } from "@/store/modules/user";
  143. const {
  144. loading,
  145. editMap,
  146. columns,
  147. dataList,
  148. options,
  149. pagination,
  150. loadingConfig,
  151. adaptiveConfig,
  152. onEdit,
  153. onSave,
  154. onCancel,
  155. onAdd,
  156. onDel,
  157. onSizeChange,
  158. onCurrentChange
  159. } = useColumns();
  160. const swEditable = ref(false);
  161. const tableKey = ref(0);
  162. const vGlobal = window.vueGlobal;
  163. const dlgImportExcelVisible = ref(false);
  164. const excelData = ref([]);
  165. const reloadProjects = () => {
  166. // vGlobal.refreshProjects(() => {
  167. // options.value = [];
  168. // options.value.push({
  169. // label: "无项目",
  170. // value: 0
  171. // });
  172. // for (const p of vGlobal.vecProject) {
  173. // options.value.push({
  174. // label: p.name,
  175. // value: p.id
  176. // });
  177. // }
  178. // console.log("reloadProjects", options.value);
  179. // tableKey.value += 1;
  180. // });
  181. options.value = [];
  182. options.value.push({
  183. label: "无项目",
  184. value: 0
  185. });
  186. for (const p of vGlobal.vecProject) {
  187. options.value.push({
  188. label: p.name,
  189. value: p.id
  190. });
  191. }
  192. console.log("reloadProjects", options.value);
  193. tableKey.value += 1;
  194. };
  195. const reloadAnchors = () => {
  196. vGlobal.refreshAnchors(() => {
  197. dataList.value = [];
  198. dataList.value = vGlobal.vecAnchor.map(item => item); // 深拷贝,触发视图更新
  199. console.log("reloadAnchors", dataList.value);
  200. tableKey.value += 1;
  201. pagination.total = dataList.value.length;
  202. });
  203. };
  204. const updateAnchors = () => {
  205. ElMessageBox.confirm("确定更新基站数据?更新后将无法撤回", "Warning", {
  206. confirmButtonText: "确认",
  207. cancelButtonText: "取消",
  208. type: "warning"
  209. })
  210. .then(() => {
  211. ElNotification.info({
  212. message: "开始更新"
  213. });
  214. const vec = [];
  215. for (const v of dataList.value) {
  216. vec.push(v);
  217. }
  218. vGlobal.updateAnchors(vec, () => {
  219. ElNotification.success({
  220. message: "更新成功"
  221. });
  222. reloadAnchors();
  223. });
  224. })
  225. .catch(() => {
  226. // ElMessage({
  227. // type: 'info',
  228. // message: 'Delete canceled',
  229. // })
  230. });
  231. };
  232. // 导出逻辑
  233. const handleExport = () => {
  234. try {
  235. // 1. 处理导出数据
  236. const exportData = dataList.value.map(item => ({
  237. // 只导出需要的字段,并映射为中文表头
  238. id: item.id,
  239. imei: item.imei,
  240. iccid: item.iccid,
  241. projId: item.projId
  242. }));
  243. if (exportData.length === 0) {
  244. ElNotification.warning({ message: "没有可导出的数据" });
  245. return;
  246. }
  247. // 2. 创建工作表
  248. const worksheet = XLSX.utils.json_to_sheet(exportData);
  249. // 3. 计算列宽(关键步骤)
  250. const header = Object.keys(exportData[0]);
  251. const cols = header.map(key => {
  252. // 计算表头文字长度
  253. let maxLength = key.length * 2;
  254. // 计算数据列最大文字长度
  255. exportData.forEach(row => {
  256. const value = String(row[key] || "");
  257. // 中文字符宽度约为英文字符的2倍,这里做特殊处理
  258. const length = value.replace(/[^\x00-\xff]/g, "aa").length;
  259. if (length > maxLength) {
  260. maxLength = length;
  261. }
  262. });
  263. // 增加缓冲宽度(避免内容太满)
  264. return { width: maxLength + 2 };
  265. });
  266. // 设置列宽配置
  267. worksheet["!cols"] = cols;
  268. // 4. 创建工作簿并添加工作表
  269. const workbook = XLSX.utils.book_new();
  270. XLSX.utils.book_append_sheet(workbook, worksheet, "基站列表");
  271. // 5. 生成Excel文件并下载
  272. const fileName = `基站列表_${new Date().getTime()}.xlsx`;
  273. XLSX.writeFile(workbook, fileName);
  274. } catch (error) {
  275. console.error("导出失败:", error);
  276. ElNotification.error({
  277. message: "导出失败,请重试"
  278. });
  279. }
  280. };
  281. const handleDlgConfirm = () => {
  282. // 将导入的数据合并到dataList
  283. dataList.value = [];
  284. for (const v of excelData.value) {
  285. dataList.value.push(v);
  286. }
  287. tableKey.value += 1;
  288. dlgImportExcelVisible.value = false;
  289. pagination.total = dataList.value.length;
  290. };
  291. const handleDlgClose = (done: () => void) => {
  292. ElMessageBox.confirm("确定要关闭这个对话框吗?")
  293. .then(() => {
  294. done();
  295. })
  296. .catch(() => {
  297. // catch error
  298. });
  299. };
  300. const onExcelData = (data: any[]) => {
  301. console.log("onExcelData", data);
  302. excelData.value = data;
  303. };
  304. onMounted(() => {
  305. reloadProjects();
  306. reloadAnchors();
  307. console.log(
  308. "exist",
  309. useUserStoreHook().existsRole(["opt", "admin", "eng", "root"])
  310. );
  311. });
  312. </script>