You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

528 lines
18 KiB

1 year ago
12 months ago
1 year ago
1 year ago
4 weeks ago
12 months ago
1 year ago
4 weeks ago
1 year ago
4 weeks ago
1 year ago
12 months ago
4 weeks ago
1 year ago
7 months ago
1 year ago
12 months ago
4 weeks ago
12 months ago
1 year ago
1 month ago
4 weeks ago
1 year ago
7 months ago
12 months ago
7 months ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
7 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
12 months ago
1 year ago
4 weeks ago
1 year ago
12 months ago
12 months ago
4 weeks ago
12 months ago
1 year ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
4 weeks ago
12 months ago
1 year ago
1 month ago
1 year ago
4 weeks ago
1 year ago
4 weeks ago
1 year ago
  1. <template>
  2. <div class="user-add-or-update">
  3. <el-dialog
  4. width="530px"
  5. :title="!dataForm.id ? buttons.add :buttons.edit"
  6. :close-on-click-modal="false"
  7. :visible.sync="visible">
  8. <!-- 取消 :rules="dataRule"-->
  9. <el-form :inline="true" :model="dataForm" label-position="top" ref="dataForm"
  10. @keyup.enter.native="dataFormSubmit()"
  11. label-width="80px">
  12. <el-form>
  13. <el-form-item :label="buttons.username||'用户账号'" prop="userName">
  14. <el-input v-model="dataForm.userName" style="width: 150px;" placeholder="登录帐号"></el-input>
  15. </el-form-item>
  16. <el-form-item :label="buttons.password||'密码'" prop="password" :class="{ 'is-required': !dataForm.id }">
  17. <el-input v-model="dataForm.password" style="width: 150px;" type="password" ></el-input>
  18. </el-form-item>
  19. <el-form-item :label="buttons.comfirmPassword||'确认密码'" prop="comfirmPassword" :class="{ 'is-required': !dataForm.id }">
  20. <el-input v-model="dataForm.comfirmPassword" style="width: 150px;" type="password"
  21. ></el-input>
  22. </el-form-item>
  23. </el-form>
  24. <el-form>
  25. <el-form-item :label="buttons.userDisplay||'用户名'" prop="email">
  26. <el-input v-model="dataForm.userDisplay" style="width: 150px;" ></el-input>
  27. </el-form-item>
  28. <el-form-item :label="buttons.domainAccount||'域控账号'" prop="domainAccount">
  29. <el-input v-model="dataForm.domainAccount" style="width: 150px;" placeholder="域控账号"></el-input>
  30. </el-form-item>
  31. <el-form-item :label="buttons.email||'邮箱'" prop="email">
  32. <el-input v-model="dataForm.email" style="width: 150px;" ></el-input>
  33. </el-form-item>
  34. <el-form-item :label="buttons.mobile||'手机号'" prop="mobile">
  35. <el-input v-model="dataForm.mobile" style="width: 150px;" placeholder="手机号"></el-input>
  36. </el-form-item>
  37. <el-form-item :label="buttons.language||'语言'" prop="languageDefault">
  38. <el-select filterable v-model="dataForm.languageDefault" clearable style="width: 150px;">
  39. <el-option :label="item.languageName" :value="item.languageCode" v-for="(item,index) in languageList "
  40. :key="index"></el-option>
  41. </el-select>
  42. </el-form-item>
  43. </el-form>
  44. <el-form>
  45. <el-form-item :label="buttons.site||'工厂'" size="mini">
  46. <el-checkbox-group v-model="selectedSiteIds" @change="handleSiteSelectionChange">
  47. <el-checkbox v-for="site in siteOptions" :key="site.key" :label="site.key">{{
  48. site.label
  49. }}
  50. </el-checkbox>
  51. </el-checkbox-group>
  52. </el-form-item>
  53. <el-form-item :label="''" size="mini" prop="siteList" class="site-auth-form-item">
  54. <div v-if="selectedSiteIds.length > 0" class="site-auth-panel">
  55. <div v-if="getSubSiteOptions(siteId).length > 0" v-for="siteId in selectedSiteIds" :key="siteId" class="site-auth-group">
  56. <div class="site-auth-group__header">
  57. <span class="site-auth-group__title">{{ getSiteDisplayName(siteId) }}</span>
  58. </div>
  59. <el-checkbox-group
  60. class="site-auth-subsite-group"
  61. v-model="subSiteCheckedMap[siteId]"
  62. @change="handleSubSiteSelectionChange(siteId)">
  63. <el-checkbox
  64. v-for="subSite in getSubSiteOptions(siteId)"
  65. :key="siteId + '_' + subSite.subSiteId"
  66. :label="subSite.subSiteId"
  67. class="site-auth-subsite-item">
  68. {{ subSite.subSiteId }}
  69. </el-checkbox>
  70. </el-checkbox-group>
  71. </div>
  72. </div>
  73. </el-form-item>
  74. </el-form>
  75. <el-form>
  76. <el-form-item label="角色" size="mini" prop="roleIdList">
  77. <el-checkbox-group v-model="dataForm.roleIdList">
  78. <el-checkbox v-for="role in roleList" :key="role.roleId" :label="role.roleId">{{
  79. role.roleName
  80. }}
  81. </el-checkbox>
  82. </el-checkbox-group>
  83. </el-form-item>
  84. </el-form>
  85. <el-form>
  86. <el-form-item label="状态" size="mini" prop="status">
  87. <el-radio-group v-model="dataForm.status">
  88. <el-radio :label="0">{{buttons.disable||'禁用'}}</el-radio>
  89. <el-radio :label="1">{{buttons.normal||'正常'}}</el-radio>
  90. </el-radio-group>
  91. </el-form-item>
  92. </el-form>
  93. </el-form>
  94. <span slot="footer" class="dialog-footer">
  95. <el-button type="primary" @click="dataFormSubmit()">{{buttons.submit||'确定'}}</el-button>
  96. <el-button @click="visible = false">{{buttons.close||'取消'}}</el-button>
  97. </span>
  98. </el-dialog>
  99. <Chooselist ref="baseList" @getBaseData="getBaseData"></Chooselist>
  100. </div>
  101. </template>
  102. <script>
  103. import Chooselist from '@/views/modules/common/Chooselist'
  104. import { getActiveSubSiteOptions, getUserAuthorizedSiteAccess } from '@/api/factory/accessSite.js'
  105. import { searchFunctionButtonList, searchSysLanguage } from '@/api/sysLanguage.js'
  106. export default {
  107. components: {
  108. Chooselist
  109. },
  110. data() {
  111. return {
  112. tagNo:'',
  113. visible: false,
  114. roleList: [],
  115. factorys: [],
  116. languageList: [],
  117. siteOptions: [], // 工厂选项列表
  118. selectedSiteIds: [], // 选中的父site
  119. subSiteOptionsMap: {}, // 子site选项映射,key=siteId
  120. subSiteCheckedMap: {}, // 已选子site映射,key=siteId
  121. dataForm: {
  122. id: 0,
  123. userName: '',
  124. password: '',
  125. comfirmPassword: '',
  126. languageDefault: 'cn',
  127. salt: '',
  128. email: '',
  129. mobile: '',
  130. roleIdList: [],
  131. status: 1,
  132. site: '', // 保持兼容性
  133. siteList: [], // 父site编码列表(兼容旧接口)
  134. siteAccessList: [], // 工厂+子site授权结构(新接口)
  135. userDisplay: '',
  136. domainAccount: ''
  137. },
  138. buttons: {
  139. add: this.$t('sys.common.add'), edit: this.$t('sys.common.edit'), username: this.$t('sys.user.account'), userDisplay: this.$t('sys.user.userName'),
  140. domainAccount: this.$t('sys.user.domainAccount'), email: this.$t('sys.user.email'), mobile: this.$t('sys.user.mobile'), status: this.$t('sys.user.status'),
  141. disable: this.$t('sys.user.disabled'), normal: this.$t('sys.user.normal'), password: this.$t('sys.user.password'), comfirmPassword: this.$t('sys.user.confirmPassword'),
  142. submit: this.$t('sys.common.confirm'), close: this.$t('sys.common.cancel'), site: this.$t('sys.user.factory'), subSite: this.$t('sys.user.subSite'), language: this.$t('sys.user.language')
  143. },
  144. }
  145. },
  146. methods: {
  147. getFunctionButtonList() {
  148. let queryButton = {
  149. functionId: this.$route.meta.menuId,
  150. tableId: '*',
  151. languageCode: this.$i18n.locale,
  152. objectType: 'button'
  153. }
  154. searchFunctionButtonList(queryButton).then(({data}) => {
  155. if (data.code == 0 && data.data) {
  156. this.buttons = data.data
  157. }
  158. })
  159. },
  160. getBaseData(val){
  161. if (this.tagNo === 93){
  162. this.dataForm.site = val.SiteID
  163. }
  164. },
  165. // 获取基础数据列表
  166. getBaseList(val){
  167. this.tagNo = val
  168. this.$nextTick(() => {
  169. let strVal = "";
  170. if (val === 93){
  171. strVal = this.dataForm.site
  172. }
  173. this.$refs.baseList.init(val,strVal)
  174. })
  175. },
  176. // 获取多语言列表
  177. getLanguageList() {
  178. let queryLanguage = { languageCode: this.$i18n.locale}
  179. searchSysLanguage(queryLanguage).then(({data}) => {
  180. this.languageList = data.rows
  181. })
  182. },
  183. init(id) {
  184. this.dataForm.id = id || 0
  185. // 如果是新增模式,清空表单数据
  186. if (!this.dataForm.id) {
  187. this.dataForm = {
  188. id: 0,
  189. userName: '',
  190. password: '',
  191. comfirmPassword: '',
  192. languageDefault: 'cn',
  193. salt: '',
  194. email: '',
  195. mobile: '',
  196. roleIdList: [],
  197. status: 1,
  198. site: '',
  199. siteList: [],
  200. siteAccessList: [],
  201. userDisplay: '',
  202. domainAccount: ''
  203. }
  204. }
  205. this.selectedSiteIds = []
  206. this.subSiteOptionsMap = {}
  207. this.subSiteCheckedMap = {}
  208. // this.factoryList();
  209. this.getLanguageList();
  210. this.getSiteOptions(); // 获取工厂选项
  211. this.$http({
  212. url: this.$http.adornUrl('/sys/role/select'),
  213. method: 'get',
  214. params: this.$http.adornParams()
  215. }).then(({data}) => {
  216. this.roleList = data && data.code === 0 ? data.list : []
  217. }).then(() => {
  218. this.visible = true
  219. this.$nextTick(() => {
  220. this.$refs['dataForm'].resetFields()
  221. })
  222. }).then(() => {
  223. if (this.dataForm.id) {
  224. // 编辑模式:加载用户数据
  225. this.$http({
  226. url: this.$http.adornUrl(`/sys/user/info/${this.dataForm.id}`),
  227. method: 'get',
  228. params: this.$http.adornParams()
  229. }).then(({data}) => {
  230. if (data && data.code === 0) {
  231. this.dataForm.userName = data.user.username
  232. this.dataForm.salt = data.user.salt
  233. this.dataForm.email = data.user.email
  234. this.dataForm.mobile = data.user.mobile
  235. this.dataForm.roleIdList = data.user.roleIdList
  236. this.dataForm.status = data.user.status
  237. this.dataForm.languageDefault = data.user.languageDefault
  238. this.dataForm.site = data.user.site.toString()
  239. this.dataForm.userDisplay = data.user.userDisplay
  240. this.dataForm.domainAccount = data.user.domainAccount
  241. // 获取用户工厂权限
  242. this.getUserSiteAccess(data.user.username)
  243. }
  244. })
  245. }
  246. })
  247. },
  248. // 表单提交
  249. dataFormSubmit() {
  250. const needSubSiteSelect = this.selectedSiteIds.find(siteId => {
  251. return this.getSubSiteOptions(siteId).length > 0 && (!this.subSiteCheckedMap[siteId] || this.subSiteCheckedMap[siteId].length === 0)
  252. })
  253. if (needSubSiteSelect) {
  254. this.$message.warning(this.$t('sys.user.selectSubSiteFirst') || '请选择子site')
  255. return
  256. }
  257. this.buildSiteAccessData()
  258. this.$refs['dataForm'].validate((valid) => {
  259. if (valid) {
  260. // 保存用户信息和工厂授权(在一个事务中)
  261. this.$http({
  262. url: this.$http.adornUrl(`/sys/user/${!this.dataForm.id ? 'save' : 'update'}`),
  263. method: 'post',
  264. data: this.$http.adornData({
  265. 'userId': this.dataForm.id || undefined,
  266. 'username': this.dataForm.userName,
  267. 'password': this.dataForm.password,
  268. 'salt': this.dataForm.salt,
  269. 'email': this.dataForm.email,
  270. 'mobile': this.dataForm.mobile,
  271. 'status': this.dataForm.status,
  272. 'roleIdList': this.dataForm.roleIdList,
  273. 'languageDefault': this.dataForm.languageDefault,
  274. 'site': this.dataForm.site,
  275. 'userDisplay': this.dataForm.userDisplay,
  276. 'domainAccount': this.dataForm.domainAccount,
  277. 'siteList': this.dataForm.siteList || [], // 兼容旧接口
  278. 'siteAccessList': this.dataForm.siteAccessList || [] // 工厂+子site授权结构
  279. })
  280. }).then(({data}) => {
  281. if (data && data.code === 0) {
  282. this.$message.success('操作成功')
  283. this.visible = false
  284. this.$emit('refreshDataList')
  285. } else {
  286. let msg = data.msg.split('<br>')[0]
  287. this.$message.error( this.$t(`${msg}`))
  288. }
  289. })
  290. }
  291. })
  292. },
  293. // 获取工厂选项列表
  294. getSiteOptions() {
  295. getActiveSubSiteOptions({}).then(({data}) => {
  296. if (data && data.code === 0) {
  297. const rows = data.data || []
  298. const siteOptionMap = {}
  299. const subSiteOptionMap = {}
  300. rows.forEach(item => {
  301. const siteId = item.siteId
  302. if (!siteId) {
  303. return
  304. }
  305. if (!siteOptionMap[siteId]) {
  306. siteOptionMap[siteId] = {
  307. key: siteId,
  308. label: item.siteName || siteId
  309. }
  310. }
  311. if (item.subSiteId) {
  312. if (!subSiteOptionMap[siteId]) {
  313. subSiteOptionMap[siteId] = []
  314. }
  315. if (!subSiteOptionMap[siteId].some(subSite => subSite.subSiteId === item.subSiteId)) {
  316. subSiteOptionMap[siteId].push({
  317. subSiteId: item.subSiteId
  318. })
  319. }
  320. }
  321. })
  322. this.siteOptions = Object.values(siteOptionMap)
  323. this.subSiteOptionsMap = subSiteOptionMap
  324. }
  325. }).catch(() => {
  326. this.$message.error('获取工厂子site列表失败')
  327. })
  328. },
  329. // 获取工厂显示名称
  330. getSiteDisplayName(siteId) {
  331. const site = this.siteOptions.find(item => item.key === siteId)
  332. return site ? `${site.label}` : siteId
  333. },
  334. // 获取指定工厂的子site列表
  335. getSubSiteOptions(siteId) {
  336. return this.subSiteOptionsMap[siteId] || []
  337. },
  338. // 选择父site后联动子site
  339. handleSiteSelectionChange() {
  340. const selectedSet = new Set(this.selectedSiteIds)
  341. Object.keys(this.subSiteCheckedMap).forEach(siteId => {
  342. if (!selectedSet.has(siteId)) {
  343. this.$delete(this.subSiteCheckedMap, siteId)
  344. }
  345. })
  346. this.selectedSiteIds.forEach(siteId => {
  347. if (!this.subSiteCheckedMap[siteId]) {
  348. // 新勾选父site时,默认勾选该site下全部子site
  349. const defaultCheckedSubSites = this.getSubSiteOptions(siteId)
  350. .map(subSite => subSite.subSiteId)
  351. .filter(Boolean)
  352. this.$set(this.subSiteCheckedMap, siteId, defaultCheckedSubSites)
  353. }
  354. })
  355. this.buildSiteAccessData()
  356. },
  357. // 选择子site后同步授权结构
  358. handleSubSiteSelectionChange(siteId) {
  359. const checkedList = this.subSiteCheckedMap[siteId] || []
  360. this.$set(this.subSiteCheckedMap, siteId, Array.from(new Set(checkedList)))
  361. this.buildSiteAccessData()
  362. },
  363. // 组装提交给后端的授权结构
  364. buildSiteAccessData() {
  365. const siteAccessList = []
  366. const siteList = []
  367. this.selectedSiteIds.forEach(siteId => {
  368. const subSiteOptions = this.getSubSiteOptions(siteId)
  369. const checkedList = this.subSiteCheckedMap[siteId] || []
  370. if (subSiteOptions.length === 0) {
  371. siteAccessList.push({
  372. userId: this.dataForm.userName,
  373. site: siteId,
  374. subSiteId: ''
  375. })
  376. siteList.push(siteId)
  377. return
  378. }
  379. checkedList.forEach(subSiteId => {
  380. if (!subSiteId) {
  381. return
  382. }
  383. siteAccessList.push({
  384. userId: this.dataForm.userName,
  385. site: siteId,
  386. subSiteId: subSiteId
  387. })
  388. siteList.push(siteId)
  389. })
  390. })
  391. this.dataForm.siteAccessList = siteAccessList
  392. this.dataForm.siteList = Array.from(new Set(siteList))
  393. // 用户主站点仍保持兼容:默认取第一个父site
  394. if (!this.dataForm.site && this.dataForm.siteList.length > 0) {
  395. this.dataForm.site = this.dataForm.siteList[0]
  396. }
  397. },
  398. // 获取用户工厂权限
  399. getUserSiteAccess(username) {
  400. if (!username) return
  401. getUserAuthorizedSiteAccess({ userName: username }).then(({data}) => {
  402. if (data && data.code === 0) {
  403. const accessRows = data.data || []
  404. const selectedSiteIds = []
  405. const checkedMap = {}
  406. accessRows.forEach(item => {
  407. let siteId = item.siteId || item.siteCode || ''
  408. if (!siteId) {
  409. return
  410. }
  411. const subSiteId = item.subSiteId || ''
  412. if (!checkedMap[siteId]) {
  413. checkedMap[siteId] = []
  414. selectedSiteIds.push(siteId)
  415. }
  416. if (subSiteId && checkedMap[siteId].indexOf(subSiteId) === -1) {
  417. checkedMap[siteId].push(subSiteId)
  418. }
  419. })
  420. this.selectedSiteIds = selectedSiteIds
  421. this.subSiteCheckedMap = {}
  422. selectedSiteIds.forEach(siteId => {
  423. this.$set(this.subSiteCheckedMap, siteId, checkedMap[siteId] || [])
  424. })
  425. this.buildSiteAccessData()
  426. }
  427. }).catch(() => {
  428. this.$message.error('获取用户工厂权限失败')
  429. })
  430. }
  431. },
  432. created() {
  433. //this.getFunctionButtonList()
  434. }
  435. }
  436. </script>
  437. <style lang="scss" scoped>
  438. .site-auth-form-item {
  439. width: 100%;
  440. }
  441. .site-auth-panel {
  442. max-height: 200px;
  443. overflow-y: auto;
  444. padding-right: 4px;
  445. }
  446. .site-auth-placeholder {
  447. color: #909399;
  448. line-height: 20px;
  449. padding: 8px 10px;
  450. border: 1px dashed #dcdfe6;
  451. border-radius: 4px;
  452. background: #fafafa;
  453. }
  454. .site-auth-group {
  455. margin-bottom: 10px;
  456. padding: 8px 10px;
  457. border: 1px solid #ebeef5;
  458. border-radius: 4px;
  459. background: #fff;
  460. }
  461. .site-auth-group:last-child {
  462. margin-bottom: 0;
  463. }
  464. .site-auth-group__header {
  465. display: flex;
  466. justify-content: space-between;
  467. align-items: center;
  468. margin-bottom: 8px;
  469. }
  470. .site-auth-group__title {
  471. color: #303133;
  472. font-weight: 500;
  473. line-height: 20px;
  474. }
  475. .site-auth-group__meta {
  476. color: #909399;
  477. font-size: 12px;
  478. line-height: 20px;
  479. }
  480. .site-auth-empty-subsite {
  481. display: flex;
  482. align-items: center;
  483. color: #e6a23c;
  484. line-height: 20px;
  485. padding: 6px 8px;
  486. border: 1px solid #faecd8;
  487. border-radius: 4px;
  488. background: #fdf6ec;
  489. }
  490. .site-auth-empty-subsite .el-icon-info {
  491. margin-right: 6px;
  492. font-size: 14px;
  493. }
  494. .site-auth-subsite-group {
  495. display: flex;
  496. flex-wrap: wrap;
  497. margin-bottom: -6px;
  498. }
  499. .site-auth-subsite-item {
  500. margin-right: 16px;
  501. margin-bottom: 6px;
  502. }
  503. </style>