user.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. <template>
  2. <HcCard>
  3. <template #header>
  4. <div class="w-40">
  5. <el-select v-model="searchForm.deptId" placeholder="选择岗位类型" clearable size="large">
  6. <el-option v-for="item in searchOrganization" :label="item.deptName" :value="item.id"/>
  7. </el-select>
  8. </div>
  9. <div class="w-64 ml-3">
  10. <el-input v-model="searchForm.realName" size="large" placeholder="请输入名称搜索" clearable/>
  11. </div>
  12. <div class="ml-3">
  13. <el-button type="primary" @click="searchClick" size="large">
  14. <HcIcon name="search-2"/>
  15. <span>搜索</span>
  16. </el-button>
  17. </div>
  18. </template>
  19. <template #extra>
  20. <el-button type="primary" @click="addUserClick" size="large">
  21. <HcIcon name="add"/>
  22. <span>创建账户</span>
  23. </el-button>
  24. <el-button type="danger" @click="delClick" size="large" :disabled="tableCheckedKeys.length <= 0">
  25. <HcIcon name="delete-bin-2"/>
  26. <span>注销账户</span>
  27. </el-button>
  28. </template>
  29. <HcTable :column="tableColumn" :datas="tableData" isCheck @selection-change="tableSelectionChange" :loading="tableLoaing">
  30. <template #status="{row}">
  31. <span>{{row.status === 1 ? '启用': '停用'}}</span>
  32. </template>
  33. <template #action="{row}">
  34. <el-button size="small" type="primary" @click="rowEidtClick(row)">编辑</el-button>
  35. </template>
  36. </HcTable>
  37. <template #action>
  38. <HcPages :pages="searchForm" @change="pageChange"></HcPages>
  39. </template>
  40. <!--用户信息弹窗-->
  41. <HcDialog bgColor="#ffffff" isToBody isTable :show="formModal" :saveText="formModel.id ? '提交保存' : '确定创建'" :title="formModel.id ? '编辑账户' : '创建账户'"
  42. @save="formModalSave" @close="formModalClose" widths="51rem"
  43. >
  44. <HcCardItem ui="hac-bg-grey" class="h-auto">
  45. <template #header>
  46. <div class="hac-card-title">基础信息</div>
  47. </template>
  48. <template #extra>
  49. <span class="text-sm text-orange" v-if="formModel.id">如果不需要修改密码,就请不要填写密码信息</span>
  50. </template>
  51. <el-form ref="formRef" :model="formModel" :rules="formRules" size="large" label-width="auto" label-position="left">
  52. <el-form-item label="登录账号:" prop="account">
  53. <el-input v-model="formModel.account" placeholder="仅支持英文或拼音" autocomplete="new-password" onkeyup="value=value.replace(/[^\w\.\/]/ig,'')" :disabled="!!formModel.id"/>
  54. </el-form-item>
  55. <el-form-item label="登录密码:" :prop="formModel.id?'':'password'">
  56. <el-input v-model="formModel.password" placeholder="请输入英文开头可包含数字的密码" autocomplete="new-password" show-password type="password" onkeyup="value=value.replace(/[^\w\.\/]/ig,'')"/>
  57. </el-form-item>
  58. <el-form-item label="确认密码:" :prop="formModel.id?'':'password1'">
  59. <el-input v-model="formModel.password1" placeholder="请再次输入密码" autocomplete="new-password" show-password type="password" onkeyup="value=value.replace(/[^\w\.\/]/ig,'')"/>
  60. </el-form-item>
  61. </el-form>
  62. </HcCardItem>
  63. <HcCardItem ui="hac-bg-grey" class="h-auto mt-4">
  64. <template #header>
  65. <div class="hac-card-title">机构信息</div>
  66. </template>
  67. <template v-for="(item, index) in formModel.deptList">
  68. <el-form :ref="(el) => setFormItemRefs(el, index)" class="mt-4" :model="item" :rules="formRules" label-position="top" size="large">
  69. <el-row :gutter="10">
  70. <el-col :span="7">
  71. <el-form-item prop="deptId">
  72. <el-select v-model="item.deptId" placeholder="选择部门" @change="initPostData($event, index)">
  73. <el-option v-for="item in sectionData" :label="item.deptName" :value="item.id"/>
  74. </el-select>
  75. </el-form-item>
  76. </el-col>
  77. <el-col :span="7">
  78. <el-form-item prop="postId" :getPostId="initPostData(item.deptId, index)">
  79. <el-select v-model="item.postId" placeholder="选择岗位">
  80. <el-option v-for="item in postData[index]" :label="item.deptName" :value="item.id"/>
  81. </el-select>
  82. </el-form-item>
  83. </el-col>
  84. <el-col :span="7">
  85. <el-form-item prop="principal">
  86. <el-select v-model="item.isLeader" placeholder="是否为部门负责人" :disabled="item.isLeaderDisabled" @change="isLeaderChange($event, index)">
  87. <el-option label="是" :value="1"/>
  88. <el-option label="否" :value="0"/>
  89. </el-select>
  90. </el-form-item>
  91. </el-col>
  92. <el-col :span="3">
  93. <div class="form-organization-row-btn">
  94. <el-button type="primary" :icon="Plus" circle size="small" @click="addOrganizationClick(item)"/>
  95. <el-button type="danger" :icon="Delete" circle size="small" :disabled="index===0" @click="delOrganizationClick(index)"/>
  96. </div>
  97. </el-col>
  98. </el-row>
  99. </el-form>
  100. </template>
  101. </HcCardItem>
  102. <HcCardItem ui="hac-bg-grey" class="h-auto mt-4">
  103. <template #header>
  104. <div class="hac-card-title">详细信息</div>
  105. </template>
  106. <el-form ref="formRef1" :model="formModel" :rules="formRules" label-position="left" label-width="auto" size="large">
  107. <el-form-item label="用户姓名:" prop="realName">
  108. <el-input v-model="formModel.realName"/>
  109. </el-form-item>
  110. <el-form-item label="手机号码:" prop="phone">
  111. <el-input v-model="formModel.phone" placeholder="请输入绑定手机"/>
  112. </el-form-item>
  113. <el-form-item label="身份证号:">
  114. <el-input v-model="formModel.idNumber"/>
  115. </el-form-item>
  116. <el-form-item label="日单价:" prop="oneMoney">
  117. <el-input v-model="formModel.oneMoney"/>
  118. </el-form-item>
  119. <el-form-item label="启用状态:" prop="status">
  120. <el-select v-model="formModel.status" class="block">
  121. <el-option label="启用" :value="1"/>
  122. <el-option label="停用" :value="0"/>
  123. </el-select>
  124. </el-form-item>
  125. </el-form>
  126. </HcCardItem>
  127. </HcDialog>
  128. </HcCard>
  129. </template>
  130. <script setup>
  131. import {ref, onMounted} from "vue";
  132. import mainApi from '~api/system/user';
  133. import organizationApi from '~api/system/organization';
  134. import {arrIndex, arrToId, formValidate, isPhone, getArrValue, isIdCard} from "js-fast-way"
  135. import {Plus, Delete} from '@element-plus/icons-vue'
  136. import {delMessage} from "~uti/tools";
  137. import {useAppStore} from "~src/store";
  138. const useAppState = useAppStore();
  139. onMounted(() => {
  140. getTableData()
  141. getOrganization()
  142. })
  143. //获取搜索时的组织结构节点
  144. const searchOrganization = ref([])
  145. const getOrganization = async () => {
  146. const { error, code, data } = await organizationApi.getList({
  147. deptType: 3
  148. })
  149. if (!error && code === 200) {
  150. searchOrganization.value = getArrValue(data)
  151. } else {
  152. searchOrganization.value = []
  153. }
  154. }
  155. const searchForm = ref({deptId: null, realName: '', current: 1, size: 20, total: 0})
  156. //分页被点击
  157. const pageChange = ({current, size}) => {
  158. searchForm.value.current = current
  159. searchForm.value.size = size
  160. getTableData()
  161. }
  162. const searchClick = () => {
  163. searchForm.value.current = 1
  164. getTableData()
  165. }
  166. //表格数据
  167. const tableColumn = [
  168. {key: 'name', name: '用户名称'},
  169. {key: 'account', name: '账号ID'},
  170. {key: 'plaintextPassword', name: '密码'},
  171. {key: 'deptName', name: '所属部门'},
  172. {key: 'postName', name: '岗位'},
  173. {key: 'createTime', name: '创建日期'},
  174. {key: 'status', name: '启用状态'},
  175. {key: 'action', name: '操作', width: 100}
  176. ]
  177. const tableData = ref([])
  178. //获取表格数据
  179. const tableLoaing = ref(false)
  180. const getTableData = async()=>{
  181. tableLoaing.value = true
  182. const { error, code, data } = await mainApi.page(searchForm.value)
  183. tableLoaing.value = false
  184. if (!error && code === 200) {
  185. tableData.value = getArrValue(data['records'])
  186. searchForm.value.total = data['total']
  187. } else {
  188. tableData.value = []
  189. searchForm.value.total = 0
  190. }
  191. }
  192. const tableCheckedKeys = ref([]);
  193. const tableSelectionChange = (rows) => {
  194. tableCheckedKeys.value = rows
  195. }
  196. //用户信息弹窗
  197. const formModal = ref(false)
  198. //用户信息表单
  199. const formRef = ref(null)
  200. const formRef1 = ref(null)
  201. //循环表单的ref
  202. const formRefs = ref([])
  203. const setFormItemRefs = (el, index) => {
  204. if (el) {
  205. let indexs = arrIndex(formRefs.value, 'index', index)
  206. if (indexs !== -1) {
  207. formRefs.value[index].ref = el
  208. } else {
  209. formRefs.value.push({index: index, ref: el});
  210. }
  211. }
  212. }
  213. //获取表单的ref
  214. const getFormRef = async (index) => {
  215. const indexs = arrIndex(formRefs.value, 'index', index)
  216. return formRefs.value[indexs].ref
  217. }
  218. const formModel = ref({
  219. deptList: [{isLeader: 0}], status: 1
  220. })
  221. const formRules = {
  222. account: [{required: true, message: '请输入登录账号', trigger: 'blur'}],
  223. password: [{required: true, message: '请输入密码', trigger: 'blur'}],
  224. password1: [{required: true, message: '请输入确认密码', trigger: 'blur'}],
  225. deptId: [{required: true, message: '请选择选择部门', trigger: 'change'}],
  226. postId: [{required: true, message: '请选择选择岗位', trigger: 'change'}],
  227. isLeader: [{required: true, message: '请选择是否为部门负责人', trigger: 'change'}],
  228. oneMoney: [{required: true, message: '请输入日单价', trigger: 'blur'}],
  229. status: [{required: true, message: '请选择启用状态', trigger: 'change'}],
  230. phone: {
  231. required: true,
  232. validator: (rule, value, callback) => {
  233. if (!value) {
  234. callback(new Error('请输入手机号'))
  235. } else if (!isPhone(value)) {
  236. callback(new Error('手机号码格式错误'))
  237. } else {
  238. callback()
  239. }
  240. },
  241. trigger: "blur"
  242. },
  243. }
  244. //添加用户
  245. const addUserClick = () => {
  246. formModel.value = {
  247. deptList: [{isLeader: 0}],
  248. tenantId: useAppState.tenantId,
  249. status: 1,
  250. userType: 5
  251. }
  252. postData.value = [{}]
  253. formModal.value = true
  254. getSectionData()
  255. }
  256. //编辑用户信息
  257. const rowEidtClick = (row) => {
  258. const deptList = getArrValue(row.deptList)
  259. //设置表单数据
  260. const form = {
  261. id: row.id,
  262. account: row.account,
  263. password: '',
  264. deptList: deptList,
  265. tenantId: useAppState.tenantId,
  266. realName: row.realName,
  267. phone: row.phone,
  268. idNumber: row.idNumber,
  269. oneMoney: row.oneMoney,
  270. status: row.status,
  271. userType: row.userType || 5
  272. }
  273. //如果部门列表为空,就添加一个空对象
  274. if (deptList.length > 0){
  275. deptList.forEach(() => {
  276. postData.value.push({})
  277. })
  278. } else {
  279. form.deptList = [{isLeader: 0}]
  280. postData.value = [{}]
  281. }
  282. //设置表单数据
  283. formModel.value = form
  284. formModal.value = true
  285. getSectionData()
  286. }
  287. //部门下拉数据
  288. const sectionData = ref([])
  289. const getSectionData = async () => {
  290. const { error, code, data } = await organizationApi.getList({deptType: 2})
  291. if (!error && code === 200) {
  292. sectionData.value = getArrValue(data)
  293. } else {
  294. sectionData.value = []
  295. }
  296. }
  297. //获取岗位数据
  298. const postData = ref([])
  299. const initPostData = async (id, index) => {
  300. if (id) {
  301. //获取岗位数据
  302. const { error, code, data } = await organizationApi.getList({parentId: id})
  303. if (!error && code === 200) {
  304. postData.value[index] = getArrValue(data)
  305. } else {
  306. postData.value[index] = []
  307. }
  308. //处理负责人限制
  309. let isLeader = 0;
  310. const deptList = formModel.value.deptList
  311. deptList.forEach((item, indexs) => {
  312. if (item.deptId === id) {
  313. //如果当前相同部门项的负责人存在,就全局存在
  314. if (item.isLeader === 1) {
  315. isLeader = 1
  316. }
  317. //设置其它相同部门项的负责人
  318. if (indexs !== index) {
  319. item.isLeader = isLeader
  320. item.isLeaderDisabled = true
  321. }
  322. }
  323. })
  324. //设置当前项的负责人
  325. deptList[index].isLeader = isLeader
  326. deptList[index].isLeaderDisabled = false
  327. }
  328. }
  329. //是否为部门负责人
  330. const isLeaderChange = (id, index) => {
  331. const deptList = formModel.value.deptList
  332. const deptId = deptList[index].deptId
  333. deptList.forEach((item) => {
  334. if (item.deptId === deptId) {
  335. item.isLeader = id
  336. }
  337. })
  338. }
  339. //新增组织
  340. const addOrganizationClick = () => {
  341. formModel.value.deptList.push({isLeader: 0})
  342. postData.value.push({})
  343. }
  344. //删除组织
  345. const delOrganizationClick = (index) => {
  346. formModel.value.deptList.splice(index, 1)
  347. postData.value.splice(index, 1)
  348. }
  349. //保存
  350. const formModalSave = async () => {
  351. const isForm = await formValidate(formRef.value)
  352. const isForm1 = await formValidate(formRef1.value)
  353. if (!isForm || !isForm1) {
  354. window.$message?.error('请先完善表单信息')
  355. return
  356. }
  357. const form = formModel.value
  358. for (let i = 0; i < form.deptList.length; i++) {
  359. const refs = await getFormRef(i)
  360. const isRefs = await formValidate(refs)
  361. if (!isRefs) {
  362. window.$message?.error('请先完善表单信息')
  363. return
  364. }
  365. }
  366. //判断密码是否一致
  367. if (form.password && !form.password1) {
  368. window.$message?.error('请输入确认密码')
  369. return
  370. }
  371. if (!form.password && form.password1) {
  372. window.$message?.error('请输入登录密码')
  373. return
  374. }
  375. if (form.password && form.password1) {
  376. if (form.password !== form.password1) {
  377. window.$message?.error('密码不一致')
  378. return
  379. } else {
  380. form.plaintextPassword = form.password
  381. }
  382. }
  383. //效验身份证号码
  384. if (form.idNumber && !isIdCard(form.idNumber)) {
  385. window.$message?.error('身份证号码格式错误')
  386. return
  387. }
  388. //发起请求
  389. if (form.id) {
  390. formUpdateApi(form).then()
  391. } else {
  392. formSubmitApi(form).then()
  393. }
  394. }
  395. //新增用户
  396. const formSubmitApi = async (form) => {
  397. const {error, code, msg} = await mainApi.submit({
  398. ...form,
  399. name: form.realName,
  400. })
  401. //判断状态
  402. if (!error && code === 200) {
  403. window.$message?.success('提交成功')
  404. getTableData().then()
  405. formModalClose()
  406. } else {
  407. window.$message?.error(msg)
  408. }
  409. }
  410. //修改用户
  411. const formUpdateApi = async (form) => {
  412. const {error, code, msg} = await mainApi.update({
  413. ...form,
  414. name: form.realName,
  415. })
  416. //判断状态
  417. if (!error && code === 200) {
  418. window.$message?.success('提交成功')
  419. getTableData().then()
  420. formModalClose()
  421. } else {
  422. window.$message?.error(msg)
  423. }
  424. }
  425. //关闭用户信息弹窗
  426. const formModalClose = () => {
  427. formModal.value = false
  428. formModel.value = {}
  429. }
  430. //删除用户
  431. const delClick = () => {
  432. delMessage(async () => {
  433. const ids = arrToId(tableCheckedKeys.value)
  434. const { error, code, msg } = await mainApi.remove(ids)
  435. if (!error && code === 200) {
  436. window?.$message?.success(msg)
  437. getTableData().then()
  438. } else {
  439. window?.$message?.error(msg)
  440. }
  441. })
  442. }
  443. </script>
  444. <style lang="scss">
  445. .hc-card-item-box.h-auto {
  446. height: auto;
  447. }
  448. .form-organization-row-btn {
  449. position: relative;
  450. margin-left: 8px;
  451. top: 7px;
  452. .el-button + .el-button {
  453. margin-left: 18px;
  454. }
  455. }
  456. </style>