index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. <template>
  2. <div class="tenant">
  3. <byTable
  4. :source="sourceList.data"
  5. :pagination="sourceList.pagination"
  6. :config="config"
  7. :loading="loading"
  8. highlight-current-row
  9. :action-list="[
  10. {
  11. text: '添加用户',
  12. action: () => openModal(),
  13. },
  14. ]"
  15. @get-list="getList">
  16. </byTable>
  17. <el-dialog :title="modalType == 'add' ? '添加用户' : '编辑用户'" v-if="dialogVisible" v-model="dialogVisible" width="600" v-loading="loadingDialog">
  18. <byForm :formConfig="formConfig" :formOption="formOption" v-model="formData.data" :rules="rules" ref="submit">
  19. <template #deptId>
  20. <div style="width: 100%">
  21. <el-tree-select
  22. v-model="formData.data.deptId"
  23. :data="deptList.data"
  24. check-strictly
  25. :render-after-expand="false"
  26. node-key="deptId"
  27. :props="defaultProps" />
  28. </div>
  29. </template>
  30. <template #account>
  31. <el-input style="width: 150px; margin-right: 10px" v-model="formData.data.userName" @change="changeUserName" placeholder="请输入用户名"></el-input>
  32. <el-input style="width: 150px; margin-right: 10px" v-model="formData.data.password" @change="changePassword" placeholder="密码"></el-input>
  33. <span style="color: #409eff; cursor: pointer" @click="newPassword">随机生成</span>
  34. </template>
  35. </byForm>
  36. <template #footer>
  37. <el-button @click="dialogVisible = false" size="large">取 消</el-button>
  38. <el-button type="primary" @click="submitForm()" size="large">确 定</el-button>
  39. </template>
  40. </el-dialog>
  41. <el-dialog title="修改密码" v-if="roomDialogVisible" v-model="roomDialogVisible" width="300" v-loading="loading">
  42. <template #footer>
  43. <el-input v-model="password" placeholder="请输入新密码" @change="changePassword2" style="margin-bottom: 20px" />
  44. <el-button @click="roomDialogVisible = false" size="large">取 消</el-button>
  45. <el-button type="primary" @click="submitPassword(password)" size="large" :loading="submitLoading">确 定</el-button>
  46. </template>
  47. </el-dialog>
  48. </div>
  49. </template>
  50. <script setup>
  51. import { computed, ref } from "vue";
  52. import byTable from "@/components/byTable/index";
  53. import { ElMessage, ElMessageBox } from "element-plus";
  54. import byForm from "@/components/byForm/index";
  55. import useUserStore from "@/store/modules/user";
  56. const { proxy } = getCurrentInstance();
  57. const defaultProps = {
  58. children: "children",
  59. label: "deptName",
  60. };
  61. const deptList = ref([]);
  62. const sourceList = ref({
  63. data: [],
  64. pagination: {
  65. total: 0,
  66. pageNum: 1,
  67. pageSize: 10,
  68. keyword: "",
  69. tenantId: useUserStore().user.tenantId,
  70. },
  71. });
  72. const loading = ref(false);
  73. const config = computed(() => {
  74. return [
  75. {
  76. attrs: {
  77. label: "部门",
  78. prop: "deptName",
  79. },
  80. },
  81. {
  82. attrs: {
  83. label: "姓名",
  84. prop: "nickName",
  85. align: "left",
  86. },
  87. },
  88. {
  89. attrs: {
  90. label: "用户名",
  91. prop: "userName",
  92. },
  93. },
  94. {
  95. attrs: {
  96. label: "系统用户",
  97. prop: "userType",
  98. },
  99. render(userType) {
  100. return userType == 1 ? "是" : "否";
  101. },
  102. },
  103. {
  104. attrs: {
  105. label: "手机号",
  106. prop: "phonenumber",
  107. },
  108. },
  109. {
  110. attrs: {
  111. label: "工号",
  112. prop: "jobNumber",
  113. },
  114. },
  115. {
  116. attrs: {
  117. label: "操作",
  118. width: "200",
  119. align: "right",
  120. },
  121. renderHTML(row) {
  122. return [
  123. {
  124. attrs: {
  125. label: "修改密码",
  126. type: "primary",
  127. text: true,
  128. },
  129. el: "button",
  130. click() {
  131. userId.value = row.userId;
  132. password.value = "";
  133. roomDialogVisible.value = true;
  134. },
  135. },
  136. {
  137. attrs: {
  138. label: "修改",
  139. type: "primary",
  140. text: true,
  141. },
  142. el: "button",
  143. click() {
  144. if (!sourceList.value.pagination.tenantId) {
  145. ElMessage({
  146. message: "请选择租户",
  147. type: "warning",
  148. });
  149. return;
  150. }
  151. getDtl(row);
  152. },
  153. },
  154. {
  155. attrs: {
  156. label: "删除",
  157. type: "danger",
  158. text: true,
  159. },
  160. el: "button",
  161. click() {
  162. ElMessageBox.confirm("此操作将永久删除该数据, 是否继续?", "提示", {
  163. confirmButtonText: "确定",
  164. cancelButtonText: "取消",
  165. type: "warning",
  166. }).then(() => {
  167. proxy
  168. .post(
  169. "/tenantUser/" + row.userId,
  170. {
  171. id: row.userId,
  172. },
  173. "delete"
  174. )
  175. .then(() => {
  176. ElMessage({
  177. message: "删除成功",
  178. type: "success",
  179. });
  180. getList();
  181. });
  182. });
  183. },
  184. },
  185. ];
  186. },
  187. },
  188. ];
  189. });
  190. const getDict = () => {
  191. proxy.get("/tenantDept/list", { pageNum: 1, pageSize: 10000, tenantId: useUserStore().user.tenantId }).then((res) => {
  192. deptList.value.data = proxy.handleTree(res.data, "deptId");
  193. setTimeout(() => {
  194. loading.value = false;
  195. }, 200);
  196. });
  197. };
  198. const getList = async (req) => {
  199. sourceList.value.pagination = { ...sourceList.value.pagination, ...req };
  200. loading.value = true;
  201. proxy.get("/tenantUser/list", sourceList.value.pagination).then((res) => {
  202. res.rows.map((item) => {
  203. item.deptName = item.dept ? item.dept.deptName : item.dept;
  204. });
  205. sourceList.value.data = res.rows;
  206. sourceList.value.pagination.total = res.total;
  207. setTimeout(() => {
  208. loading.value = false;
  209. }, 200);
  210. });
  211. };
  212. getDict();
  213. getList();
  214. const modalType = ref("add");
  215. const dialogVisible = ref(false);
  216. const loadingDialog = ref(false);
  217. const submit = ref(null);
  218. const formOption = reactive({
  219. inline: true,
  220. labelWidth: 100,
  221. itemWidth: 100,
  222. rules: [],
  223. });
  224. const formData = reactive({
  225. data: {},
  226. });
  227. const formConfig = computed(() => {
  228. return [
  229. {
  230. type: "slot",
  231. prop: "deptId",
  232. slotName: "deptId",
  233. label: "部门名称",
  234. },
  235. {
  236. type: "input",
  237. prop: "nickName",
  238. label: "姓名",
  239. },
  240. {
  241. type: "slot",
  242. prop: "userName",
  243. slotName: "account",
  244. label: "账户信息",
  245. },
  246. {
  247. type: "radio",
  248. prop: "userType",
  249. label: "系统用户",
  250. required: true,
  251. disabled: true,
  252. border: true,
  253. data: [
  254. {
  255. label: "是",
  256. id: 1,
  257. },
  258. {
  259. label: "否",
  260. id: 0,
  261. },
  262. ],
  263. },
  264. {
  265. type: "select",
  266. label: "角色",
  267. prop: "roleIds",
  268. multiple: true,
  269. data: [],
  270. },
  271. {
  272. type: "input",
  273. prop: "phonenumber",
  274. label: "手机号",
  275. required: true,
  276. itemWidth: 50,
  277. itemType: "text",
  278. },
  279. {
  280. type: "input",
  281. prop: "jobNumber",
  282. label: "工号",
  283. required: true,
  284. itemWidth: 50,
  285. itemType: "text",
  286. },
  287. ];
  288. });
  289. const rules = ref({
  290. deptId: [{ required: true, message: "请选择部门名称", trigger: "change" }],
  291. nickName: [{ required: true, message: "请输入姓名", trigger: "blur" }],
  292. userName: [{ required: true, message: "请输入用户名", trigger: "blur" }],
  293. roleIds: [{ required: true, message: "请选择角色", trigger: "change" }],
  294. phonenumber: [{ required: true, message: "请输入手机号", trigger: "blur" }],
  295. });
  296. const getUserList = () => {
  297. proxy.get(`/tenantRole/list?pageNum=1&pageSize=10000&tenantId=${sourceList.value.pagination.tenantId}`).then((message) => {
  298. formConfig.value[4].data = message.rows.map((item) => {
  299. return {
  300. ...item,
  301. id: item.roleId,
  302. label: item.roleName,
  303. };
  304. });
  305. });
  306. };
  307. getUserList();
  308. const openModal = () => {
  309. modalType.value = "add";
  310. formData.data = {
  311. userType: 1,
  312. tenantId: useUserStore().user.tenantId,
  313. };
  314. loadingDialog.value = false;
  315. dialogVisible.value = true;
  316. };
  317. const submitForm = () => {
  318. submit.value.handleSubmit(() => {
  319. if (formData.data.password && formData.data.password.length < 5) {
  320. return ElMessage("密码长度不得低于五位");
  321. }
  322. const method = modalType.value == "add" ? "POST" : "PUT";
  323. proxy.post("/tenantUser", formData.data, method).then(() => {
  324. if (formData.data.password && formData.data.userId) {
  325. proxy.post("/tenantUser/resetPwd", { password: formData.data.password, userId: formData.data.userId }, "PUT").then();
  326. }
  327. ElMessage({
  328. message: modalType.value == "add" ? "添加成功" : "编辑成功",
  329. type: "success",
  330. });
  331. dialogVisible.value = false;
  332. getList();
  333. });
  334. });
  335. };
  336. const getDtl = (row) => {
  337. dialogVisible.value = true;
  338. proxy.get(`/tenantUser/${row.userId}`).then((res) => {
  339. formData.data = { ...row, roleIds: res.roleIds };
  340. modalType.value = "edit";
  341. });
  342. };
  343. const newPassword = () => {
  344. formData.data.password = generatePassword1();
  345. };
  346. const generatePassword1 = () => {
  347. //随机生成一个密码,必须包含大小写和数字,且不能有连续三个相同的字符
  348. const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
  349. const passwordLength = 10; // 密码长度,你可以根据需要调整
  350. var password1 = '';
  351. for (let i = 0; i < passwordLength; i++) {
  352. // 随机选择一个字符
  353. const randomChar = chars.charAt(Math.floor(Math.random() * chars.length));
  354. // 如果前两个字符是相同的,并且当前字符不是大写,那么我们就选择一个大写字符
  355. if (i < 2 && password1.length < 2 && randomChar === password1[password1.length - 1]) {
  356. continue;
  357. }
  358. // 如果前三个字符是相同的,并且当前字符是大写,那么我们就选择一个小写字符
  359. if (i < 3 && password1.length < 3 && randomChar === password1[password1.length - 1] && randomChar === password1[password1.length - 2] && randomChar.toUpperCase() === password1[password1.length - 2]) {
  360. continue;
  361. }
  362. password1 += randomChar;
  363. }
  364. return password1;
  365. };
  366. const userId = ref("");
  367. const password = ref("");
  368. const roomDialogVisible = ref(false);
  369. const submitPassword = (password1) => {
  370. //正则判断密码构成 必须有大写小写数字,而且不能有连续三个相同的字符
  371. const reg = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?!.*([a-zA-Z0-9])\1{2}).{8,16}$/;
  372. if (!reg.test(password1)) {
  373. ElMessage({
  374. message: "密码必须有大写小写数字,而且不能有连续三个相同的字符",
  375. type: "warning",
  376. });
  377. return;
  378. }
  379. proxy
  380. .post(
  381. "/tenantUser/resetPwd",
  382. {
  383. password: password1,
  384. userId: userId.value,
  385. },
  386. "PUT"
  387. )
  388. .then(() => {
  389. ElMessage({
  390. message: "重置成功",
  391. type: "success",
  392. });
  393. roomDialogVisible.value = false;
  394. password.value = "";
  395. });
  396. };
  397. const changeUserName = (val) => {
  398. formData.data.userName = val.trim();
  399. };
  400. const changePassword = (val) => {
  401. formData.data.password = val.trim();
  402. };
  403. const changePassword2 = (val) => {
  404. password.value = val.trim();
  405. };
  406. </script>
  407. <style lang="scss" scoped>
  408. .tenant {
  409. padding: 20px;
  410. }
  411. ::v-deep(.el-input-number .el-input__inner) {
  412. text-align: left;
  413. }
  414. </style>