初始化
This commit is contained in:
		
							
								
								
									
										247
									
								
								core/apps/AppAccount/AppAccount.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								core/apps/AppAccount/AppAccount.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,247 @@ | ||||
| <template> | ||||
|   <section class="AppAccount"> | ||||
|     <ai-list> | ||||
|       <ai-title slot="title" title="账号管理" isShowBottomBorder/> | ||||
|       <template #left> | ||||
|         <ai-address-book-menu :instance="instance" @select="handleSelect"/> | ||||
|       </template> | ||||
|       <template #content> | ||||
|         <ai-title :title="tableTitle"/> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button type="primary" :disabled="!ids.toString()" @click="batchAllot">功能分配</el-button> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input size="small" placeholder="搜索姓名、手机号" v-model="search.name" clearable | ||||
|                       @change="page.current=1,getTableData()"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="tableData" :total="page.total" :current.sync="page.current" :size.sync="page.size" | ||||
|                   @getList="getTableData" :col-configs="colConfigs" :dict="dict" | ||||
|                   @selection-change="v=>ids=v.filter(e=>e.sysUserId).map(e=>e.sysUserId)"> | ||||
|           <el-table-column slot="name" label="姓名" width="180px"> | ||||
|             <el-row type="flex" align="middle" slot-scope="{row}"> | ||||
|               <el-image class="avatar" :src="row.avatar" :preview-src-list="[row.avatar]"> | ||||
|                 <el-image slot="error" src="https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png" alt=""/> | ||||
|               </el-image> | ||||
|               <div>{{ row.name }}</div> | ||||
|             </el-row> | ||||
|           </el-table-column> | ||||
|           <el-table-column slot="options" align="center" label="操作" fixed="right" width="160px"> | ||||
|             <el-row type="flex" justify="center" align="middle" slot-scope="{row}"> | ||||
|               <el-button v-if="$permissions('admin_sysuser_distribute')&&!!row.sysUserId" | ||||
|                          type="text" @click="appAllot(row)">功能分配 | ||||
|               </el-button> | ||||
|             </el-row> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <!--功能分配--> | ||||
|     <ai-dialog title="功能分配" :visible.sync="dialog" width="800px" @open="initDialogData" @onConfirm="updateAccount"> | ||||
|       <el-form ref="updateAccountForm" :model="dialogForm" :rules="rules" size="small" | ||||
|                label-width="120px"> | ||||
|         <el-form-item required label="角色" prop="roleId"> | ||||
|           <el-select size="small" placeholder="请选择角色" :value="dialogForm.roleId" filterable | ||||
|                      v-model="dialogForm.roleId" clearable> | ||||
|             <el-option v-for="(op,i) in accountRoles" :key="i" :label="op.name" :value="op.id"/> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="行政地区" prop="areaId"> | ||||
|           <ai-area-select v-model="dialogForm.areaId" always-show :instance="instance" | ||||
|                           clearable @fullname="v=>dialogForm.areaFullName=v" | ||||
|                           @name="v=>dialogForm.areaName=v" | ||||
|                           :disabledLevel="disabledLevel"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="党组织" prop="organizationId" v-if="user.info.organizationId"> | ||||
|           <el-cascader :options="partyOrgOps" v-model="dialogForm.organizationId" | ||||
|                        :props="cascaderProps" :show-all-levels="false" clearable/> | ||||
|         </el-form-item> | ||||
|         <!--        <el-form-item label="职务" prop="position">--> | ||||
|         <!--          <el-input placeholder="请输入职务" v-model="dialogForm.position" clearable/>--> | ||||
|         <!--        </el-form-item>--> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
| import AiAddressBookMenu from "../../components/AiAddressBookMenu"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppAccount", | ||||
|   components: {AiAddressBookMenu}, | ||||
|   label: "账号管理(村微sass版)", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     cascaderProps() { | ||||
|       return { | ||||
|         value: 'id', | ||||
|         checkStrictly: true, | ||||
|         emitPath: false | ||||
|       } | ||||
|     }, | ||||
|     partyOrgOps() { | ||||
|       let initData = JSON.parse(JSON.stringify(this.optionsParty)), | ||||
|           ops = initData.filter(e => !e.parentId) | ||||
|       ops.map(e => this.addChild(e, initData)) | ||||
|       return ops | ||||
|     }, | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {type: 'selection', align: 'center'}, | ||||
|         {label: "姓名", slot: "name"}, | ||||
|         {label: "职务", prop: "position", align: 'center', width: "120px"}, | ||||
|         {label: "部门", prop: "departmentNames", align: 'center', width: "120px"}, | ||||
|         {label: "联系方式", prop: "mobile", align: 'center', width: "120px"}, | ||||
|         {label: "账号状态", prop: "status", dict: "wxUserStatus", width: "120px"}, | ||||
|         {label: "账号角色", prop: "roleName", width: "120px", align: '120px'}, | ||||
|         {label: "地区", prop: "areaName", width: "120px"}, | ||||
|         {label: "党组织", prop: "organizationName", align: 'center', width: "120px"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     }, | ||||
|     tableTitle() { | ||||
|       return this.condition ? this.condition + `(${this.page.total})` : '请选择组织或标签' | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         name: [{required: true, message: "请填写姓名"}], | ||||
|         // organizationId: [{required: true, message: "请选择党组织"}], | ||||
|         // unitId: [{required: true, message: "请选择单位"}], | ||||
|         areaId: [{required: true, message: '请选择地区', trigger: 'change'}], | ||||
|         roleId: [{required: true, message: "请选择角色"}], | ||||
|         phone: [{required: true, message: "请输入手机号码"}] | ||||
|       } | ||||
|     }, | ||||
|     disabledLevel() { | ||||
|       return this.user.info.areaList?.length || 0 | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       condition: "", | ||||
|       accountRoles: [], | ||||
|       page: {current: 1, size: 10, total: 0}, | ||||
|       dialog: false, | ||||
|       dialogForm: {}, | ||||
|       optionsParty: [], | ||||
|       tableData: [], | ||||
|       search: {name: ""}, | ||||
|       ids: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getTableData() { | ||||
|       this.instance.post("/app/wxcp/wxuser/list", null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.tableData = res.data?.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleSelect(v) { | ||||
|       if (v.type == 0) { | ||||
|         //选择部门 | ||||
|         let {id: departmentId, name} = v | ||||
|         this.condition = name | ||||
|         this.search = {departmentId} | ||||
|       } else if (v.type == 1) { | ||||
|         //选择标签 | ||||
|         let {id: tagIds, tagname: name} = v | ||||
|         this.condition = name | ||||
|         this.search = {tagIds} | ||||
|       } | ||||
|       this.page.current = 1 | ||||
|       this.getTableData() | ||||
|     }, | ||||
|     initDialogData() { | ||||
|       //用于优化初始化数据 | ||||
|       this.getAccountRoles() | ||||
|       this.searchSysAll() | ||||
|     }, | ||||
|     getAccountRoles() { | ||||
|       this.accountRoles.length == 0 && this.instance.post("/admin/role-acc/list-all").then(res => { | ||||
|         if (res?.data) { | ||||
|           this.accountRoles = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     batchAllot() { | ||||
|       this.dialog = true | ||||
|       this.dialogForm = {areaId: this.user.info.areaId, sysUserIds: this.ids} | ||||
|     }, | ||||
|     appAllot(row) { | ||||
|       this.dialog = true | ||||
|       this.dialogForm = JSON.parse(JSON.stringify({ | ||||
|         ...row, | ||||
|         sysUserIds: [row.sysUserId], | ||||
|         areaId: row.areaId || this.user.info.areaId | ||||
|       })); | ||||
|     }, | ||||
|     // 获取党组织树形 | ||||
|     searchSysAll() { | ||||
|       if (this.user.info.organizationId && this.optionsParty.length == 0) { | ||||
|         this.instance.post('/app/partyOrganization/queryPartyOrganizationServiceList').then((res) => { | ||||
|           if (res?.data) { | ||||
|             res.data = res.data.map(a => { | ||||
|               return {...a, label: a.name} | ||||
|             }); | ||||
|             this.optionsParty = res.data.filter(e => !e.parentId) | ||||
|             this.optionsParty.map(t => this.addChild(t, res.data)); | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|     // 修改 | ||||
|     updateAccount() { | ||||
|       this.$refs.updateAccountForm.validate(v => { | ||||
|         if (v) { | ||||
|           this.instance.post("/app/wxcp/wxuser/empower", this.dialogForm).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.dialog = false; | ||||
|               this.$message.success("修改成功") | ||||
|               this.getTableData(); | ||||
|             } else { | ||||
|               this.$message.error(res?.msg) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.dict.load('wxUserStatus') | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppAccount { | ||||
|   height: 100%; | ||||
|  | ||||
|   ::v-deep .avatar { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|     margin-right: 10px; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .ai-list__content--left { | ||||
|     margin-right: 2px; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .el-form { | ||||
|     .el-cascader { | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										156
									
								
								core/apps/AppDictionary/AppDictionary.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								core/apps/AppDictionary/AppDictionary.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| <template> | ||||
|   <section class="AppDictionary"> | ||||
|     <ai-list v-if="!showDetail"> | ||||
|       <ai-title slot="title" title="数据字典" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button type="primary" size="small" icon="iconfont iconAdd" @click="addDict" | ||||
|                        v-if="$permissions('admin_sysdictionary_add')">添加 | ||||
|             </el-button> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input size="small" v-model="search.condition" placeholder="数据项" clearable | ||||
|                       @change="page.current=1,getDicts()" prefix-icon="iconfont iconSearch"/> | ||||
|             <el-button type="primary" size="small" icon="iconfont iconSearch" | ||||
|                        @click="page.current=1,getDicts()">查询 | ||||
|             </el-button> | ||||
|             <el-button size="small" icon="el-icon-refresh-right" @click="resetSearch">重置</el-button> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <el-table size="mini" :data="dictList" header-cell-class-name="table-header" tooltip-effect="light" | ||||
|                   row-class-name="table-row" cell-class-name="table-cell" @expand-change="getDictInfo"> | ||||
|           <el-table-column type="expand"> | ||||
|             <el-row slot-scope="{row}" type="flex" align="middle" style="flex-wrap: wrap"> | ||||
|               <el-tag v-for="(op,i) in row.detail||[]" :key="i" style="margin: 4px">{{ op.dictValue }}|{{ op.dictName }} | ||||
|                 {{ op.dictColor ? '| ' + op.dictColor : '' }} | ||||
|               </el-tag> | ||||
|             </el-row> | ||||
|           </el-table-column> | ||||
|           <el-table-column align="center" label="数据项" prop="code"/> | ||||
|           <el-table-column align="center" label="数据项名称" prop="name"/> | ||||
|           <el-table-column align="center" label="操作"> | ||||
|             <div slot-scope="{row}"> | ||||
|               <el-button type="text" @click="openDetail(row.id)" v-text="'编辑'" | ||||
|                          v-if="$permissions('admin_sysdictionary_edit')"/> | ||||
|               <el-button type="text" @click="handleDelete(row.id)" v-text="'删除'" | ||||
|                          v-if="$permissions('admin_sysdictionary_del')"/> | ||||
|             </div> | ||||
|           </el-table-column> | ||||
|           <div slot="empty" class="no-data"></div> | ||||
|         </el-table> | ||||
|         <div class="pagination"> | ||||
|           <el-pagination background :current-page.sync="page.current" :total="page.total" | ||||
|                          layout="total,prev, pager, next,sizes, jumper" | ||||
|                          @size-change="handleSizeChange" | ||||
|                          :page-size="page.size" | ||||
|                          :page-sizes="[10, 20, 50, 100,200]" | ||||
|                          @current-change="getDicts"/> | ||||
|         </div> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <dict-detail v-else :instance="instance" :permissions="permissions"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import DictDetail from "./dictDetail"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppDictionary", | ||||
|   components: {DictDetail}, | ||||
|   label: "数据字典", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     showDetail() { | ||||
|       return this.$route.hash == "#add" | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       page: { | ||||
|         current: 1, | ||||
|         total: 0, | ||||
|         size: 10 | ||||
|       }, | ||||
|       search: { | ||||
|         condition: "" | ||||
|       }, | ||||
|       dictList: [], | ||||
|       id: '' | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     resetSearch() { | ||||
|       this.page.current = 1; | ||||
|       this.search.condition = ''; | ||||
|       this.getDicts(); | ||||
|     }, | ||||
|     getDicts() { | ||||
|       this.instance.post("/admin/dictionary/queryDictList", null, { | ||||
|         params: { | ||||
|           ...this.page, | ||||
|           name: this.search.condition | ||||
|         } | ||||
|       }).then(res => { | ||||
|         this.dictList = res.data.records.map(e => { | ||||
|           return {...e, detail: []} | ||||
|         }) | ||||
|         this.page.total = res.data.total | ||||
|       }) | ||||
|     }, | ||||
|     addDict() { | ||||
|       this.$router.push({hash: "#add"}) | ||||
|     }, | ||||
|     handleDelete(id) { | ||||
|       this.$confirm("确定要删除该数据项吗?", { | ||||
|         type: "error" | ||||
|       }).then(() => { | ||||
|         this.instance.post("/admin/dictionary/deleteDict", null, { | ||||
|           params: {id} | ||||
|         }).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.getDicts(); | ||||
|             this.$message.success("删除成功!") | ||||
|           } | ||||
|         }) | ||||
|       }).catch(() => 0) | ||||
|     }, | ||||
|     openDetail(id) { | ||||
|       this.$router.push({query: {id}, hash: "#add"}) | ||||
|     }, | ||||
|     handleSizeChange(val) { | ||||
|       this.page.size = val; | ||||
|       this.getDicts(); | ||||
|     }, | ||||
|     getDictInfo(row) { | ||||
|       if (row.detail.length) { | ||||
|         row.detail = [] | ||||
|       } else { | ||||
|         this.getDict(row.id).then(res => { | ||||
|           if (res && res.data) { | ||||
|             row.detail = res.data.dictionaryDetails || [] | ||||
|           } | ||||
|         }) | ||||
|       } | ||||
|     }, | ||||
|     getDict(dictionaryId) { | ||||
|       return this.instance.post("/admin/dictionary/queryDictDetail", null, { | ||||
|         params: {dictionaryId} | ||||
|       }) | ||||
|     }, | ||||
|   }, | ||||
|   created() { | ||||
|     this.getDicts() | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppDictionary { | ||||
|   height: 100%; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										202
									
								
								core/apps/AppDictionary/dictDetail.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								core/apps/AppDictionary/dictDetail.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| <template> | ||||
|   <section class="dictDetail"> | ||||
|     <ai-detail> | ||||
|       <ai-title slot="title" title="字典信息" isShowBottomBorder isShowBack @onBackClick="$router.push({})"/> | ||||
|       <template #content> | ||||
|         <ai-card title="基本信息"> | ||||
|           <template #content> | ||||
|             <el-form ref="dictDetailForm" :model="form" :rules="rules" size="small" label-width="110px"> | ||||
|               <el-form-item required label="数据项:" prop="code"> | ||||
|                 <el-input v-model="form.code" style="width: 259px;" clearable | ||||
|                           placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|               <el-form-item required label="数据项名称:" prop="name"> | ||||
|                 <el-input v-model="form.name" style="width: 259px;" clearable | ||||
|                           placeholder="请输入..."/> | ||||
|               </el-form-item> | ||||
|             </el-form> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="数据值" v-if="$route.query.id"> | ||||
|           <template #right> | ||||
|             <el-button type="text" icon="iconfont iconAdd" | ||||
|                        @click="form.dictionaryDetails.push({name:'',value:'',editable:true})"> 添加 | ||||
|             </el-button> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <el-table border :data="form.dictionaryDetails" header-cell-class-name="table-header" | ||||
|                       cell-class-name="table-cell"> | ||||
|               <el-table-column align="center" label="值"> | ||||
|                 <div slot-scope="{row}"> | ||||
|                   <el-input size="small" v-if="row.editable" v-model="row.value" clearable/> | ||||
|                   <span v-else>{{ row.dictValue }}</span> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|               <el-table-column align="center" label="描述"> | ||||
|                 <div slot-scope="{row}"> | ||||
|                   <el-input size="small" v-if="row.editable" v-model="row.name" clearable/> | ||||
|                   <span v-else>{{ row.dictName }}</span> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|               <el-table-column align="center" label="颜色"> | ||||
|                 <div slot-scope="{row}"> | ||||
|                   <el-color-picker v-if="row.editable" v-model="row.dictColor" size="medium"></el-color-picker> | ||||
|                   <span v-else>{{ row.dictColor || '未设置' }}</span> | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|               <el-table-column align="center" label="操作" width="109px"> | ||||
|                 <div slot-scope="{row,$index}"> | ||||
|                   <section v-if="row.editable"> | ||||
|                     <el-button style="color: #2EA222" type="text" icon="iconfont iconCorrect" | ||||
|                                @click="addDict(row)"/> | ||||
|                     <el-button style="color: #f46" type="text" icon="iconfont iconClean" | ||||
|                                @click="cancelEdit(row,$index)"/> | ||||
|                   </section> | ||||
|                   <section v-else> | ||||
|                     <el-button class="dict-detail-operation" type="text" icon="iconfont iconEdit" | ||||
|                                @click="editDetail(row)"/> | ||||
|                     <el-button class="dict-detail-operation" type="text" icon="iconfont iconDelete" | ||||
|                                @click="delDictValue(row.id)"/> | ||||
|                   </section> | ||||
|  | ||||
|                 </div> | ||||
|               </el-table-column> | ||||
|             </el-table> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </template> | ||||
|       <template #footer> | ||||
|         <el-button @click="$router.push({})">返回</el-button> | ||||
|         <el-button type="primary" @click="modifyDict">保存</el-button> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| export default { | ||||
|   name: "dictDetail", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     rules() { | ||||
|       return { | ||||
|         code: [ | ||||
|           {required: true, message: "请填写数据项"} | ||||
|         ], | ||||
|         name: [ | ||||
|           {required: true, message: "请填写数据项名称"} | ||||
|         ], | ||||
|         // dictionaryDetails: [ | ||||
|         //   { | ||||
|         //     validator: (r, v, cb) => { | ||||
|         //       if (v.every(item => item.dictName && item.dictValue)) { | ||||
|         //         cb() | ||||
|         //       } | ||||
|         //     } | ||||
|         //   } | ||||
|         // ] | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: { | ||||
|         code: "", | ||||
|         name: "", | ||||
|         dictionaryDetails: [] | ||||
|       }, | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     if (this.$route.query.id) this.getDict() | ||||
|   }, | ||||
|   methods: { | ||||
|     getDict() { | ||||
|       this.instance.post("/admin/dictionary/queryDictDetail", null, { | ||||
|         params: {dictionaryId: this.$route.query.id} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           res.data.dictionaryDetails = res.data.dictionaryDetails.map(d => { | ||||
|             return { | ||||
|               ...d, | ||||
|               editable: false, | ||||
|               name: "", | ||||
|               value: "" | ||||
|             } | ||||
|           }) | ||||
|           this.form = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     delDictValue(id) { | ||||
|       this.$confirm("是否要删除该字典值", { | ||||
|         type: 'error' | ||||
|       }).then(() => { | ||||
|         this.instance.post("/admin/dictionary/deletevalue", null, { | ||||
|           params: {id} | ||||
|         }).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.$message.success("删除成功!") | ||||
|             this.getDict() | ||||
|           } | ||||
|         }) | ||||
|       }).catch(() => 0) | ||||
|     }, | ||||
|     editDetail(row) { | ||||
|       row.editable = true | ||||
|       row.name = row.dictName | ||||
|       row.value = row.dictValue | ||||
|     }, | ||||
|     addDict(row) { | ||||
|       row.dictValue = row.value | ||||
|       row.dictName = row.name | ||||
|       row.dictionaryId = this.form.id | ||||
|       this.instance.post("/admin/dictionary/updateDetail", row).then(res => { | ||||
|         row.editable = false | ||||
|         row = res.data.data | ||||
|         this.$message.success("提交成功!") | ||||
|       }) | ||||
|     }, | ||||
|     cancelEdit(row, index) { | ||||
|       if (row.id) { | ||||
|         row.editable = false | ||||
|       } else { | ||||
|         this.form.dictionaryDetails.splice(index, 1) | ||||
|       } | ||||
|     }, | ||||
|     modifyDict() { | ||||
|       this.$refs.dictDetailForm.validate(v => { | ||||
|         if (v) { | ||||
|           this.instance.post("/admin/dictionary/updateDict", this.form).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$message.success("提交成功!") | ||||
|               this.$router.push({}) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .dictDetail { | ||||
|   height: 100%; | ||||
|  | ||||
|   ::v-deep .el-table__row { | ||||
|  | ||||
|     .el-input__inner { | ||||
|       padding: 0 30px; | ||||
|       border: none; | ||||
|       text-align: center; | ||||
|       background: #ddd; | ||||
|       font-size: 14px; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										344
									
								
								core/apps/AppQyWxConfig/AppQyWxConfig.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										344
									
								
								core/apps/AppQyWxConfig/AppQyWxConfig.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,344 @@ | ||||
| <template> | ||||
|   <section class="AppQyWxConfig"> | ||||
|     <ai-list> | ||||
|       <ai-title slot="title" title="企业微信配置" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button type="primary" @click="add" icon="iconfont iconAdd">新增</el-button> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input size="small" placeholder="搜索名称" v-model="search.name" clearable | ||||
|                       @change="page.current=1,getTableData()"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="tableData" :total="page.total" :current.sync="page.current" :size.sync="page.size" | ||||
|                   @getList="getTableData" :col-configs="colConfigs"> | ||||
|           <el-table-column slot="status" align="center" label="状态" width="150"> | ||||
|             <template v-slot="{ row }"> | ||||
|               <el-switch v-model="row.status" @change="onChange(row)" active-value="1" inactive-value="0" active-color="#5088FF" inactive-color="#D0D4DC"> </el-switch> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|           <el-table-column slot="options" align="center" label="操作" fixed="right" width="200px"> | ||||
|             <el-row type="flex" justify="center" align="middle" slot-scope="{row}"> | ||||
|               <el-button type="text" @click="detail(row)">详情</el-button> | ||||
|               <el-button type="text" @click="del(row)">删除</el-button> | ||||
|             </el-row> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|  | ||||
|     <ai-dialog title="新增" :visible.sync="dialog" width="800px" @onConfirm="confirm"> | ||||
|       <el-form ref="form" :model="dialogForm" :rules="rules" size="small" label-width="180px"> | ||||
|         <el-form-item required label="名称" prop="name"> | ||||
|           <el-input v-model.trim="dialogForm.name" placeholder="请输入名称" show-word-limit maxlength="100"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="企业微信ID" prop="corpId"> | ||||
|           <el-input v-model.trim="dialogForm.corpId" placeholder="请输入企业微信ID" show-word-limit maxlength="32"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="企业微信通讯录SECRET" prop="corpAddressBookSecret"> | ||||
|           <el-input v-model.trim="dialogForm.corpAddressBookSecret" placeholder="请输入企业微信通讯录SECRET" show-word-limit maxlength="64"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="企业微信AESKEY" prop="corpAeskey"> | ||||
|           <el-input v-model.trim="dialogForm.corpAeskey" placeholder="请输入企业微信AESKEY" show-word-limit maxlength="64"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="企业微信AGENTID" prop="corpAgentId"> | ||||
|           <el-input v-model.trim="dialogForm.corpAgentId" placeholder="请输入企业微信AGENTID" show-word-limit maxlength="10"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="企业微信SECRET" prop="corpSecret"> | ||||
|           <el-input v-model.trim="dialogForm.corpSecret" placeholder="请输入企业微信SECRET" show-word-limit maxlength="64"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="企业微信TOKEN" prop="corpToken"> | ||||
|           <el-input v-model.trim="dialogForm.corpToken" placeholder="请输入企业微信TOKEN" show-word-limit maxlength="32"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="小程序APPID" prop="miniappAppid"> | ||||
|           <el-input v-model.trim="dialogForm.miniappAppid" placeholder="请输入小程序APPID" show-word-limit maxlength="32"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="小程序SECRET" prop="miniappSecret"> | ||||
|           <el-input v-model.trim="dialogForm.miniappSecret" placeholder="请输入小程序SECRET" show-word-limit maxlength="32"></el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="访问域名" prop="dvcpUrl"> | ||||
|           <el-input v-model.trim="dialogForm.dvcpUrl" placeholder="请输入访问域名" show-word-limit maxlength="128"> | ||||
|             <template slot="prepend">Http://</template> | ||||
|           </el-input> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="地区" prop="areaId"> | ||||
|           <ai-area-select :instance="instance" v-model="dialogForm.areaId" alwaysShow  @name="(e)=>dialogForm.areaName=e"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="地图中心点" prop="lat"> | ||||
|           <el-button type="primary" icon="iconfont iconAdd" @click="showMap=true">设置地点</el-button> | ||||
|           <div v-if="dialogForm.lat">{{dialogForm.address}}</div> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="状态" prop="status"> | ||||
|           <el-radio-group v-model.trim="dialogForm.status"> | ||||
|             <el-radio label="1">启用</el-radio> | ||||
|             <el-radio label="0">禁用</el-radio> | ||||
|           </el-radio-group> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|  | ||||
|     <ai-dialog title="地图" :visible.sync="showMap" @opened="initMap" width="800px" class="mapDialog" @onConfirm="selectMap"> | ||||
|       <div id="map"></div> | ||||
|       <el-input id="searchPlaceInput" size="medium" class="searchPlaceInput" clearable v-model="searchPlace" autocomplete="on" | ||||
|                 @change="placeSearch.search(searchPlace)"> | ||||
|         <el-button type="primary" slot="append" @click="placeSearch.search(searchPlace)">搜索</el-button> | ||||
|       </el-input> | ||||
|       <div id="searchPlaceOutput"/> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
|   import {mapState} from "vuex"; | ||||
|   import AMapLoader from "@amap/amap-jsapi-loader" | ||||
|  | ||||
|   export default { | ||||
|     name: "AppQyWxConfig", | ||||
|     label: "企业微信配置", | ||||
|     props: { | ||||
|       instance: Function, | ||||
|       dict: Object, | ||||
|       permissions: Function | ||||
|     }, | ||||
|     computed: { | ||||
|       ...mapState(['user']), | ||||
|       colConfigs() { | ||||
|         return [ | ||||
|           {prop: "name", label: "名称",width: 150}, | ||||
|           {prop: "corpId", label: "企业微信ID",width: 180}, | ||||
|           {prop: "corpAddressBookSecret", label: "企业微信通讯录SECRET",width: 200}, | ||||
|           {prop: "corpAgentId", label: "企业微信AGENTID",width: 150}, | ||||
|           {prop: "corpSecret", label: "企业微信SECRET",width: 200}, | ||||
|           {prop: "corpToken", label: "企业微信TOKEN",width: 150}, | ||||
|           {prop: "corpAeskey", label: "企业微信AESKEY",width: 150}, | ||||
|           {prop: "miniappAppid", label: "小程序APPID",width: 150}, | ||||
|           {prop: "miniappSecret", label: "小程序SECRET",width: 150}, | ||||
|           {prop: "lat", label: "纬度",width: 100}, | ||||
|           {prop: "lng", label: "经度",width: 100}, | ||||
|           {prop: "address", label: "中心点",width: 100}, | ||||
|           {slot: "status",}, | ||||
|           {prop: "createTime", label: "创建时间",width: 150}, | ||||
|           {slot: "options"}, | ||||
|         ] | ||||
|       }, | ||||
|       rules() { | ||||
|         return { | ||||
|           name: [{required: true, message: "请填写名称"}], | ||||
|           corpId: [{required: true, message: "请填写企业微信ID"}], | ||||
|           corpAddressBookSecret: [{required: true, message: "请填写企业微信通讯录SECRET"}], | ||||
|           corpAeskey: [{required: true, message: "请填写企业微信AESKEY"}], | ||||
|           corpAgentId: [{required: true, message: "请填写企业微信AGENTID"}], | ||||
|           corpSecret: [{required: true, message: "请填写企业微信SECRET"}], | ||||
|           corpToken: [{required: true, message: "请填写企业微信TOKEN"}], | ||||
|           miniappAppid: [{required: true, message: "请填写小程序APPID"}], | ||||
|           miniappSecret: [{required: true, message: "请填写小程序SECRET"}], | ||||
|           dvcpUrl: [{required: true, message: "请填写访问域名"}], | ||||
|           status: [{required: true, message: "请选择状态",trigger:"change"}], | ||||
|           areaId: [{required: true, message: "请选择地区",trigger:"change"}], | ||||
|           lat: [{required: true, message: "请选择中心点"}], | ||||
|         } | ||||
|       }, | ||||
|     }, | ||||
|     data() { | ||||
|       return { | ||||
|         page: {current: 1, size: 10, total: 0}, | ||||
|         dialog: false, | ||||
|         showMap: false, | ||||
|         map: null, | ||||
|         placeSearch: null, | ||||
|         placeDetail: {}, | ||||
|         searchPlace: "", | ||||
|         dialogForm: {}, | ||||
|         tableData: [], | ||||
|         search: { | ||||
|           name: "" | ||||
|         }, | ||||
|       } | ||||
|     }, | ||||
|     methods: { | ||||
|       selectMap() { | ||||
|         Object.keys(this.placeDetail).map(e=>this.dialogForm[e] = this.placeDetail[e]); | ||||
|         this.showMap = false; | ||||
|       }, | ||||
|       initMap(){ | ||||
|         AMapLoader.load({ | ||||
|           key: '54a02a43d9828a8f9cd4f26fe281e74e', | ||||
|           version: '2.0', | ||||
|           plugins: ['AMap.PlaceSearch', 'AMap.AutoComplete', 'AMap.Geocoder'], | ||||
|         }).then(AMap=>{ | ||||
|           this.map = new AMap.Map('map', { | ||||
|             resizeEnable: true, | ||||
|             zooms: [6, 20], | ||||
|             center: [116.394681, 39.910283], | ||||
|             zoom: 11 | ||||
|           }) | ||||
|           this.placeSearch = new AMap.PlaceSearch({map: this.map}) | ||||
|           new AMap.AutoComplete({ | ||||
|             input: "searchPlaceInput", | ||||
|             output: 'searchPlaceOutput', | ||||
|           }).on('select', e => { | ||||
|             if (e?.poi) { | ||||
|               this.placeSearch.setCity(e.poi.adcode); | ||||
|               this.movePosition(e.poi.location) | ||||
|             } | ||||
|           }) | ||||
|           this.map.on('click', e => { | ||||
|             new AMap.Geocoder().getAddress(e.lnglat, (sta, res) => { | ||||
|               if (res?.regeocode) { | ||||
|                 this.placeDetail = { | ||||
|                   lng: e.lnglat?.lng, | ||||
|                   lat: e.lnglat?.lat, | ||||
|                   address: res.regeocode.formattedAddress | ||||
|                 } | ||||
|               } | ||||
|             }) | ||||
|             this.movePosition(e.lnglat) | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|       movePosition(center) { | ||||
|         if (this.map) { | ||||
|           this.map.clearMap() | ||||
|           this.map.panTo(center) | ||||
|           this.map.add([ | ||||
|             new AMap.Marker({ | ||||
|               position: center, | ||||
|               clickable: true | ||||
|             }) | ||||
|           ]) | ||||
|           this.map.setFitView() | ||||
|         } | ||||
|       }, | ||||
|       onChange(row) { | ||||
|         this.instance.post(`/app/appdvcpconfig/setStatus`,null,{ | ||||
|           params:{ | ||||
|             id:row.id, | ||||
|             status: row.status | ||||
|           } | ||||
|         }).then((res) => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success(+row.status ? '已启用' : '已禁用'); | ||||
|             this.getTableData(); | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       add() { | ||||
|         this.dialogForm = {}; | ||||
|         this.dialog = true; | ||||
|       }, | ||||
|       del(row){ | ||||
|         this.$confirm("是否要删除?").then(_=>{ | ||||
|           this.instance.post("/app/appdvcpconfig/delete",null,{ | ||||
|             params: { | ||||
|               ids:row.id | ||||
|             } | ||||
|           }).then(res=>{ | ||||
|             if(res.code==0){ | ||||
|               this.$message.success("删除成功"); | ||||
|               this.dialog=false; | ||||
|               this.getTableData(); | ||||
|             } | ||||
|           }) | ||||
|         }) | ||||
|       }, | ||||
|       detail(row){ | ||||
|         this.instance.post("/app/appdvcpconfig/detail",null,{ | ||||
|           params:{ | ||||
|             id:row.id | ||||
|           } | ||||
|         }).then(res=>{ | ||||
|           if(res && res.data) { | ||||
|             this.dialogForm = {...res.data}; | ||||
|             this.dialog = true; | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|       getTableData() { | ||||
|         this.instance.post("/app/appdvcpconfig/list", null, { | ||||
|           params: {...this.page, ...this.search} | ||||
|         }).then(res => { | ||||
|           if (res?.data) { | ||||
|             this.tableData = res.data?.records | ||||
|             this.page.total = res.data.total | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|  | ||||
|       confirm() { | ||||
|         this.$refs["form"].validate(valid => { | ||||
|           if (valid) { | ||||
|             this.instance.post("/app/appdvcpconfig/addOrUpdate", { | ||||
|               ...this.dialogForm, | ||||
|             }).then(res => { | ||||
|               if (res.code == 0) { | ||||
|                 this.$message.success(this.dialogForm.id ? "修改成功" : "新增成功"); | ||||
|                 this.dialog=false; | ||||
|                 this.getTableData(); | ||||
|               } | ||||
|             }) | ||||
|           } | ||||
|         }) | ||||
|       }, | ||||
|     }, | ||||
|     created() { | ||||
|       this.dict.load("integralRuleStatus").then(this.getTableData); | ||||
|     } | ||||
|   } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
|   .AppQyWxConfig { | ||||
|     height: 100%; | ||||
|  | ||||
|  | ||||
|     ::v-deep .mapDialog{ | ||||
|       .el-dialog__body { | ||||
|         padding: 0; | ||||
|  | ||||
|         .ai-dialog__content { | ||||
|           padding: 0; | ||||
|         } | ||||
|  | ||||
|         .ai-dialog__content--wrapper { | ||||
|           padding: 0 !important; | ||||
|           position: relative; | ||||
|         } | ||||
|  | ||||
|         #map { | ||||
|           width: 100%; | ||||
|           height: 500px; | ||||
|         } | ||||
|  | ||||
|         .searchPlaceInput { | ||||
|           position: absolute; | ||||
|           width: 250px; | ||||
|           top: 30px; | ||||
|           left: 25px; | ||||
|         } | ||||
|  | ||||
|         #searchPlaceOutput { | ||||
|           position: absolute; | ||||
|           width: 250px; | ||||
|           left: 25px; | ||||
|           height: initial; | ||||
|           top: 80px; | ||||
|           background: white; | ||||
|           z-index: 250; | ||||
|           max-height: 300px; | ||||
|           overflow-y: auto; | ||||
|  | ||||
|           .auto-item { | ||||
|             text-align: left; | ||||
|             font-size: 14px; | ||||
|             padding: 8px; | ||||
|             box-sizing: border-box; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|       } | ||||
|     } | ||||
|  | ||||
|   } | ||||
| </style> | ||||
							
								
								
									
										443
									
								
								core/apps/AppRightsManager/AppRightsManager.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										443
									
								
								core/apps/AppRightsManager/AppRightsManager.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,443 @@ | ||||
| <template> | ||||
|   <section class="AppRightsManager"> | ||||
|     <ai-list v-if="!showDetail"> | ||||
|       <ai-title slot="title" title="权限管理" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <ai-select placeholder="请选择应用" v-model="search.appId" :selectList="appList" | ||||
|                        @change="searchList"/> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input | ||||
|                 size="small" | ||||
|                 v-model="search.roleName" | ||||
|                 placeholder="角色名称" | ||||
|                 clearable | ||||
|                 @change="searchList()" | ||||
|                 suffix-icon="iconfont iconSearch"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button size="small" type="primary" icon="iconfont iconAdd" | ||||
|                        @click="$router.push({hash:'#add'})" | ||||
|                        v-if="$permissions('admin_sysapprole_add')"> | ||||
|               添加 | ||||
|             </el-button> | ||||
|             <el-button | ||||
|                 size="small" | ||||
|                 icon="iconfont iconDelete" | ||||
|                 :disabled="!multipleSelection.length" | ||||
|                 class="del-btn-list" | ||||
|                 @click="deleteApp('all')" | ||||
|                 v-if="$permissions('admin_sysapprole_del')" | ||||
|             >删除 | ||||
|             </el-button> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="adminList" :colConfigs="colConfigs" :total="total" :current.sync="page.pageNum" | ||||
|                   :size.sync="page.pageSize" | ||||
|                   @getList="getTableData" :col-configs="colConfigs" :dict="dict" | ||||
|                   @selection-change="v=>multipleSelection=v"> | ||||
|           <el-table-column label="角色用户" slot="users" align="center"> | ||||
|             <template slot-scope="scope"> | ||||
|               <el-tooltip | ||||
|                   effect="light" | ||||
|                   placement="top" | ||||
|                   :disabled="scope.row.users.length <= 2" | ||||
|                   content="更多角色用户请点击详情按钮"> | ||||
|               <span v-if="scope.row.users.length"> | ||||
|                 {{ | ||||
|                   scope.row.users | ||||
|                   .slice(0, 2) | ||||
|                   .map((e) => e.name + "(" + e.phone + ")") | ||||
|                   .join(";") | ||||
|                 }} | ||||
|                 <span v-if="scope.row.users.length > 2">...</span> | ||||
|               </span> | ||||
|                 <span v-else>-</span> | ||||
|               </el-tooltip> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|           <el-table-column slot="options" label="操作" fixed="right" align="center"> | ||||
|             <template slot-scope="{row}"> | ||||
|               <el-button type="text" @click="beforeCopy(row)" v-if="$permissions('admin_sysapprole_edit')">复制 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="viewApp(row)" v-if="$permissions('admin_sysapprole_detail')">详情 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="openRightsGraph(row)" v-if="$permissions('admin_sysapprole_detail')">关系图 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="toAddAppRole(row)" v-if="$permissions('admin_sysapprole_edit')">编辑 | ||||
|               </el-button> | ||||
|               <el-button type="text" @click="deleteApp(row)" v-if="$permissions('admin_sysapprole_del')">删除</el-button> | ||||
|             </template> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|         <ai-dialog | ||||
|             title="应用角色详情" | ||||
|             :visible.sync="viewShow" | ||||
|             width="600px" | ||||
|             customFooter> | ||||
|           <ai-card title="基本信息"> | ||||
|             <template #content> | ||||
|               <ai-wrapper> | ||||
|                 <ai-info-item label="应用角色名称" :value="viewInfo.name"/> | ||||
|                 <ai-info-item label="应用名称" :value="viewInfo.appName"/> | ||||
|               </ai-wrapper> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card title="权限信息"> | ||||
|             <template #content> | ||||
|               <ai-wrapper> | ||||
|                 <ai-info-item :label="viewInfo.appName" isLine> | ||||
|                   {{ roleList.map(e => e.label).join('、') }} | ||||
|                 </ai-info-item> | ||||
|               </ai-wrapper> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <ai-card title="角色账号"> | ||||
|             <template #right> | ||||
|             <span style="text-align: right; color: #999" | ||||
|             >共<span | ||||
|                 style="color: #26f" | ||||
|                 v-text="userList.length" | ||||
|             />个账号</span | ||||
|             > | ||||
|             </template> | ||||
|             <template #content> | ||||
|               <div class="datail-table-body" v-if="userList.length"> | ||||
|                 <div class="datail-item" v-for="(item, index) in userList" :key="index"> | ||||
|                   <span class="item-name">{{ item.name }}</span> | ||||
|                   <span style="color: #999">{{ item.phone }}</span> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </template> | ||||
|           </ai-card> | ||||
|           <template #footer> | ||||
|             <el-button | ||||
|                 type="primary" | ||||
|                 @click="toAddAppRole(viewInfo)" | ||||
|                 v-if="$permissions('admin_sysapprole_edit')" | ||||
|             >编辑角色 | ||||
|             </el-button> | ||||
|           </template> | ||||
|         </ai-dialog> | ||||
|         <ai-dialog title="权限关系图" :visible.sync="rightsGraph" class="rightsGraphDialog" customFooter> | ||||
|           <rights-graph :instance="instance" :dict="dict" :app="selectApp"/> | ||||
|           <el-button slot="footer" @click="rightsGraph=false">关闭</el-button> | ||||
|         </ai-dialog> | ||||
|         <!--复制角色--> | ||||
|         <el-dialog | ||||
|             class="editStyle" | ||||
|             :visible.sync="copyDialog" | ||||
|             width="520px" | ||||
|             @close="dataInit()" | ||||
|             title="复制角色"> | ||||
|           <el-form :model="form" label-width="80px"> | ||||
|             <el-form-item label="角色名" :rules="[{ required: true, message: '', trigger: 'blur' }]"> | ||||
|               <el-input | ||||
|                   v-model="editName" | ||||
|                   placeholder="请输入..." | ||||
|                   size="small" | ||||
|                   clearable | ||||
|               /> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|           <div slot="footer" style="text-align: center"> | ||||
|             <el-button | ||||
|                 style="width: 92px" | ||||
|                 size="small" | ||||
|                 @click="copyDialog = false" | ||||
|             >取消 | ||||
|             </el-button | ||||
|             > | ||||
|             <el-button | ||||
|                 style="width: 92px" | ||||
|                 size="small" | ||||
|                 type="primary" | ||||
|                 @click="copyFn()" | ||||
|                 :disabled="!editName" | ||||
|             > | ||||
|               确认 | ||||
|             </el-button> | ||||
|           </div> | ||||
|         </el-dialog> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <rights-add v-else :instance="instance" :dict="dict" :permissions="permissions"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import RightsAdd from "./rightsAdd"; | ||||
| import RightsGraph from "./rightsGraph"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppRightsManager", | ||||
|   components: {RightsGraph, RightsAdd}, | ||||
|   label: "权限管理", | ||||
|   provide() { | ||||
|     return { | ||||
|       top: this | ||||
|     } | ||||
|   }, | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function, | ||||
|     actions: { | ||||
|       default: () => ({ | ||||
|         list: '/admin/role-app/page', | ||||
|         apps: '/admin/role-app/list-all', | ||||
|         delete: '/admin/role-app/del', | ||||
|         detail: '/admin/role-app/queryById-checked', | ||||
|         modify: '/admin/role-app/modify', | ||||
|       }) | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         {type: "selection"}, | ||||
|         {label: "应用", prop: "appName", width: '120px'}, | ||||
|         {label: "角色名", prop: "name", width: '100px'}, | ||||
|         {label: "用户数量", prop: "roleCount", align: 'center', width: '80px'}, | ||||
|         {slot: "users"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     }, | ||||
|     showDetail() { | ||||
|       return this.$route.hash == "#add" | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       page: {pageNum: 1, pageSize: 10}, | ||||
|       search: {appId: '', roleName: ''}, | ||||
|       adminList: [], //列表数据 | ||||
|       total: 0, | ||||
|       appList: [], //下拉选择列表 | ||||
|       multipleSelection: [], | ||||
|       delShow: false, | ||||
|       delParams: "", | ||||
|       delIds: [], | ||||
|       viewShow: false, | ||||
|       viewInfo: {}, | ||||
|       roleList: [], //详情权限列表 | ||||
|       row: {}, | ||||
|       copyDialog: false, | ||||
|       titleDel: "", | ||||
|       form: {}, | ||||
|       editName: "", | ||||
|       userList: [], | ||||
|       rightsGraph: false, | ||||
|       selectApp: {} | ||||
|     }; | ||||
|   }, | ||||
|   created() { | ||||
|     this.getTableData(); | ||||
|     this.getAppList(); | ||||
|   }, | ||||
|   methods: { | ||||
|     //查询table列表 | ||||
|     getTableData() { | ||||
|       this.adminList = []; | ||||
|       this.instance.post(this.actions.list, null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.total = res.data.total; | ||||
|           this.adminList = res.data.records; | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     //查询下拉列表 | ||||
|     getAppList() { | ||||
|       this.instance.post(this.actions.apps).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.appList = res.data?.map(e => ({dictName: e.name, dictValue: e.id})) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     //查询 | ||||
|     searchList() { | ||||
|       this.page.pageNum = 1; | ||||
|       this.getTableData(); | ||||
|     }, | ||||
|     //添加按钮 | ||||
|     toAddAppRole(item) { | ||||
|       this.$router.push({ | ||||
|         hash: "#add", | ||||
|         query: { | ||||
|           id: item.id, | ||||
|           name: item.name, | ||||
|           appId: item.appId, | ||||
|         }, | ||||
|       }); | ||||
|     }, | ||||
|     //删除 | ||||
|     deleteApp(e) { | ||||
|       if (e == "all") { | ||||
|         this.multipleSelection.map((item) => { | ||||
|           this.delIds.push(item.id); | ||||
|         }); | ||||
|         this.delParams = `ids=${this.delIds}`; | ||||
|         this.titleDel = "确定要执行删除操作吗?"; | ||||
|       } else { | ||||
|         this.delParams = `ids=${e.id}`; | ||||
|         this.titleDel = "确定需要删除该角色吗?"; | ||||
|       } | ||||
|       this.$confirm(this.titleDel, { | ||||
|         type: "error", | ||||
|       }).then(() => { | ||||
|         this.instance.post(`${this.actions.delete}?${this.delParams}`).then(res => { | ||||
|           if (res?.msg == "success") { | ||||
|             this.getTableData(); | ||||
|           } else { | ||||
|             this.$message.error(res.msg); | ||||
|           } | ||||
|         }); | ||||
|       }).catch(() => 0); | ||||
|     }, | ||||
|     //查看信息 | ||||
|     viewApp(e) { | ||||
|       this.userList = e.users; | ||||
|       this.viewInfo = e; | ||||
|       this.viewShow = true; | ||||
|       this.getRowInfo(this.viewInfo.appId, this.viewInfo.id); | ||||
|     }, | ||||
|     //查询 row 信息 | ||||
|     getRowInfo(appId, id) { | ||||
|       this.roleList = []; | ||||
|       this.instance.post(`${this.actions.detail}?id=${appId}&roleId=${id}`) | ||||
|       .then(res => { | ||||
|         if (res?.data) { | ||||
|           this.roleList = res.data.list.filter(e => e.checked) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     //复制 | ||||
|     beforeCopy(row) { | ||||
|       this.row = row; | ||||
|       this.copyDialog = true; | ||||
|       this.getRowInfo(this.row.appId, this.row.id); | ||||
|     }, | ||||
|     //确认复制 | ||||
|     copyFn() { | ||||
|       let crr = []; | ||||
|       let appRoleList = this.roleList; | ||||
|       for (let i = 0; i < appRoleList.length; i++) { | ||||
|         if (appRoleList[i].checked) { | ||||
|           crr.push(appRoleList[i].id); | ||||
|           if (appRoleList[i].list.length) { | ||||
|             for (let j = 0; j < appRoleList[i].list.length; j++) { | ||||
|               if (appRoleList[i].list[j].checked) { | ||||
|                 crr.push(appRoleList[i].list[j].id); | ||||
|               } | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|       this.instance.post(`${this.actions.modify}?menus=${crr}`, null, { | ||||
|         params: { | ||||
|           roleName: this.editName, | ||||
|           appId: this.row.appId, | ||||
|         }, | ||||
|       }) | ||||
|       .then((res) => { | ||||
|         if (res.code == 0) { | ||||
|           this.$message({message: "复制成功", type: "success"}); | ||||
|           this.copyDialog = false; | ||||
|           this.searchList() | ||||
|         } | ||||
|       }); | ||||
|     }, | ||||
|     dataInit() { | ||||
|       this.multipleSelection = []; | ||||
|       this.row = {}; | ||||
|     }, | ||||
|     handleSelectionChange(val) { | ||||
|       this.multipleSelection = val; | ||||
|     }, | ||||
|     openRightsGraph(row) { | ||||
|       this.rightsGraph = true | ||||
|       this.selectApp = row | ||||
|     } | ||||
|   }, | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppRightsManager { | ||||
|   height: 100%; | ||||
|  | ||||
|  | ||||
|   ::v-deep .ai-dialog { | ||||
|     .ai-card { | ||||
|       box-shadow: none; | ||||
|       border: 1px solid #eee; | ||||
|  | ||||
|       .aibar { | ||||
|         height: 40px; | ||||
|         background: #f3f6f9; | ||||
|       } | ||||
|  | ||||
|       .ai-card__body { | ||||
|         padding: 0 16px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .rightsGraphDialog { | ||||
|     .el-dialog__body { | ||||
|       padding: 0; | ||||
|     } | ||||
|  | ||||
|     .ai-dialog__content { | ||||
|       padding-bottom: 0; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   ::v-deep .datail-table-body { | ||||
|     width: 100%; | ||||
|     height: auto; | ||||
|     margin-bottom: 16px; | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
|  | ||||
|     .datail-item { | ||||
|       flex-shrink: 0; | ||||
|       width: 50%; | ||||
|       height: 24px; | ||||
|       line-height: 24px; | ||||
|  | ||||
|       span { | ||||
|         display: inline-block; | ||||
|         font-size: 12px; | ||||
|       } | ||||
|  | ||||
|       .item-name { | ||||
|         width: 102px; | ||||
|         padding-left: 16px; | ||||
|         color: #333; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     .datail-item:nth-of-type(2n - 1) { | ||||
|       border-right: 1px solid rgba(208, 212, 220, 1); | ||||
|       width: calc(50% - 1px); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .padd-l0 { | ||||
|     padding-left: 0 !important; | ||||
|   } | ||||
|  | ||||
|   .pad-l16 { | ||||
|     padding-left: 16px; | ||||
|   } | ||||
|  | ||||
|  | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										195
									
								
								core/apps/AppRightsManager/rightsAdd.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								core/apps/AppRightsManager/rightsAdd.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,195 @@ | ||||
| <template> | ||||
|   <ai-detail class="rightsAdd"> | ||||
|     <ai-title :title="addTitle" slot="title" isShowBottomBorder isShowBack @onBackClick="back"/> | ||||
|     <template #content> | ||||
|       <el-form size="small" ref="rightsForm" :model="form" label-width="120px" :rules="rules"> | ||||
|         <ai-card title="基本信息"> | ||||
|           <template #content> | ||||
|             <el-form-item label="应用角色名称" prop="roleName"> | ||||
|               <el-input v-model="form.roleName" placeholder="请输入应用角色名称" clearable/> | ||||
|             </el-form-item> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="权限信息"> | ||||
|           <template #content> | ||||
|             <el-form-item label="应用" prop="appId"> | ||||
|               <ai-select placeholder="请选择应用" v-model="form.appId" :selectList="top.appList" | ||||
|                          @change="getPermissions"/> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="权限列表" prop="menus" v-if="form.appId"> | ||||
|               <div class="roleList"> | ||||
|                 <el-input v-model="filterText" placeholder="请输入..." clearable suffix-icon="iconfont iconSearch" | ||||
|                           @change="$refs.tree.filter(filterText)" :validate-event="false"/> | ||||
|                 <div class="tree_list"> | ||||
|                   <el-tree class="filter-tree" ref="roleTree" | ||||
|                            :data="roleList" | ||||
|                            show-checkbox | ||||
|                            :props="defaultProps" | ||||
|                            default-expand-all | ||||
|                            :check-strictly="false" | ||||
|                            node-key="id" | ||||
|                            :default-checked-keys="form.menus" | ||||
|                            :filter-node-method="filterNode" | ||||
|                            @check="handleMenusSelect"/> | ||||
|                 </div> | ||||
|               </div> | ||||
|             </el-form-item> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </el-form> | ||||
|     </template> | ||||
|     <template #footer> | ||||
|       <el-button @click="back()">取消</el-button> | ||||
|       <el-button type="primary" @click="confirm">保存</el-button> | ||||
|     </template> | ||||
|   </ai-detail> | ||||
| </template> | ||||
| <script> | ||||
|  | ||||
| export default { | ||||
|   name: "rightsAdd", | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: {}, | ||||
|       roleName: '', | ||||
|       id: '', | ||||
|       appList: [], | ||||
|       roleList: [], | ||||
|       defaultProps: { | ||||
|         children: 'list', | ||||
|         label: 'name' | ||||
|       }, | ||||
|       treeList: [], | ||||
|       filterText: '', | ||||
|       msgTitle: '添加', | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     if (this.isEdit) { | ||||
|       let {id, appId, name: roleName} = this.$route.query | ||||
|       this.form = {appId, menus: [], id, roleName} | ||||
|       this.getPermissions() | ||||
|       this.msgTitle = '编辑' | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     isEdit() { | ||||
|       return this.$route.query.id | ||||
|     }, | ||||
|     addTitle() { | ||||
|       return this.isEdit ? '编辑应用角色' : '新增应用角色' | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         roleName: {required: true, message: '请输入应用角色名称'}, | ||||
|         appId: {required: true, message: '请选择应用'}, | ||||
|         menus: {required: true, message: '请选择权限列表内容'}, | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     filterNode(value, data) { | ||||
|       if (!value) return true; | ||||
|       return data.name.indexOf(value) !== -1; | ||||
|     }, | ||||
|     //应用名称选择 获取权限列表 | ||||
|     getId(data) { | ||||
|       if (data.list.length) { | ||||
|         data.list.forEach(item => { | ||||
|           this.getId(item) | ||||
|         }) | ||||
|       } else { | ||||
|         if (data.checked) { | ||||
|           this.form.menus?.push(data.id) | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     getPermissions() { | ||||
|       this.filterText = '' | ||||
|       let {appId: id, id: roleId} = this.form | ||||
|       this.instance.post(this.top.actions.detail, null, { | ||||
|         params: {id, roleId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.roleList = [res.data]; | ||||
|           if (this.isEdit) { | ||||
|             this.roleList.forEach(e => this.getId(e)) | ||||
|           } | ||||
|           this.roleList = this.roleList.filter(item => !(item.component && item.isApp == 0 && item.isMenu == 0)) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleMenusSelect(node, selected) { | ||||
|       this.$set(this.form, 'menus', [...selected?.checkedKeys]) | ||||
|       this.$refs.rightsForm.validateField('menus') | ||||
|     }, | ||||
|     //保存提交 | ||||
|     confirm() { | ||||
|       this.$refs.rightsForm.validate(v => { | ||||
|         if (v) { | ||||
|           let menus = [this.$refs.roleTree?.getHalfCheckedKeys(), this.$refs.roleTree?.getCheckedKeys()]?.flat()?.toString() | ||||
|           this.instance.post(this.top.actions.modify, null, { | ||||
|             params: {...this.form, menus} | ||||
|           }).then(res => { | ||||
|             if (res?.msg == "success") { | ||||
|               this.$message.success(`${this.msgTitle}应用角色成功`) | ||||
|               this.back() | ||||
|               this.top.searchList() | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     //取消 返回 | ||||
|     back() { | ||||
|       this.$router.push({}) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
| <style lang="scss" scoped> | ||||
| .rightsAdd { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   position: relative; | ||||
|  | ||||
|   .el-form-item { | ||||
|     .el-select { | ||||
|       width: 100%; | ||||
|     } | ||||
|  | ||||
|     &.is-error { | ||||
|       .roleList { | ||||
|         border-color: #f46; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   .roleList { | ||||
|     background-color: #fcfcfc; | ||||
|     border-radius: 2px; | ||||
|     border: solid 1px #d0d4dc; | ||||
|     padding: 8px; | ||||
|  | ||||
|     .input { | ||||
|       display: flex; | ||||
|       justify-content: space-between; | ||||
|       padding: 5px; | ||||
|       margin: 0; | ||||
|     } | ||||
|  | ||||
|     .tree_list { | ||||
|       padding: 5px; | ||||
|       height: 370px; | ||||
|       overflow: auto; | ||||
|     } | ||||
|   } | ||||
|  | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										192
									
								
								core/apps/AppRightsManager/rightsGraph.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								core/apps/AppRightsManager/rightsGraph.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| <template> | ||||
|   <section class="rightsGraph"> | ||||
|     <div id="RightGraph"/> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import * as echarts from "echarts"; | ||||
|  | ||||
| export default { | ||||
|   name: "rightsGraph", | ||||
|   inject: ['top'], | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     app: Object | ||||
|   }, | ||||
|   computed: { | ||||
|     graphData() { | ||||
|       let data = [...this.users, ...this.nodes].map(e => { | ||||
|         if (e.x) { | ||||
|           return e | ||||
|         } else return {...e, ...this.renderPosition(e)} | ||||
|       }) | ||||
|       return [ | ||||
|         { | ||||
|           data, | ||||
|           links: this.links, | ||||
|           categories: data.map(e => e.category).flat().map(name => ({name})) | ||||
|         } | ||||
|       ] | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       graph: null, | ||||
|       nodes: [], | ||||
|       links: [], | ||||
|       users: [] | ||||
|     } | ||||
|   }, | ||||
|   watch: { | ||||
|     graphData: { | ||||
|       deep: true, handler() { | ||||
|         this.refreshGraph() | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     initGraph() { | ||||
|       let dom = document.querySelector("#RightGraph") | ||||
|       if (dom) { | ||||
|         this.graph = echarts.init(dom) | ||||
|         this.graph.setOption({ | ||||
|           tooltip: {}, | ||||
|           series: [ | ||||
|             { | ||||
|               type: 'graph', | ||||
|               layout: 'none', | ||||
|               roam: true, | ||||
|               label: { | ||||
|                 show: true, | ||||
|                 position: 'right', | ||||
|                 formatter: '{b}' | ||||
|               }, | ||||
|               labelLayout: { | ||||
|                 hideOverlap: true, | ||||
|               }, | ||||
|               scaleLimit: { | ||||
|                 min: 0.4, | ||||
|                 max: 4 | ||||
|               }, | ||||
|               lineStyle: { | ||||
|                 color: 'target', | ||||
|                 curveness: 0.1 | ||||
|               }, | ||||
|               emphasis: { | ||||
|                 focus: 'adjacency', | ||||
|                 lineStyle: { | ||||
|                   width: 5 | ||||
|                 } | ||||
|               } | ||||
|             } | ||||
|           ] | ||||
|         }) | ||||
|         this.graph.on('click', this.handleNodeClick) | ||||
|       } | ||||
|     }, | ||||
|     refreshGraph() { | ||||
|       this.graph?.setOption({ | ||||
|         series: this.graphData | ||||
|       }) | ||||
|     }, | ||||
|     getAppRoles(role) { | ||||
|       if (role) { | ||||
|         this.nodes.push({...role, category: '应用角色', value: "应用角色", symbolSize: 15}) | ||||
|       } else this.instance.post(this.top.actions.list, null, { | ||||
|         params: {pageSize: 999} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           res.data.records.map(e => { | ||||
|             this.getUsers(e.users, e.id) | ||||
|             this.nodes.push({...e, category: '应用角色', value: "应用角色"}) | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     getUsers(pending, source) { | ||||
|       pending?.map(e => { | ||||
|         if (!this.users.some(u => u.id == e.phone)) { | ||||
|           this.users.push({id: e.phone, name: e.name, symbolSize: 5, category: '用户', value: "用户"}) | ||||
|         } | ||||
|         this.links.push({source, target: e.phone}) | ||||
|       }) | ||||
|     }, | ||||
|     getPermissions({appId: id, id: roleId, name: category, dataIndex}) { | ||||
|       const addNodes = (list, source, category, pos) => { | ||||
|         list?.map(e => { | ||||
|           let node = { | ||||
|             ...e, | ||||
|             symbolSize: 5, | ||||
|             category, | ||||
|             value: e.list?.length > 0 ? "应用" : "权限", | ||||
|           } | ||||
|           node = {...node, ...this.renderPosition(pos || node)} | ||||
|           this.nodes.splice(dataIndex, 0, node) | ||||
|           this.links.push({source, target: e.id}) | ||||
|           addNodes(e.list, e.id, e.label, node) | ||||
|         }) | ||||
|       } | ||||
|       id && this.instance.post(this.top.actions.detail, null, { | ||||
|         params: {id, roleId} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           addNodes(res.data.list, roleId, category) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleNodeClick(v) { | ||||
|       let {data: role, dataIndex} = v | ||||
|       if (!this.nodes.some(e => e.category == role?.name)) { | ||||
|         role && this.getPermissions({...role, dataIndex}) | ||||
|       } | ||||
|     }, | ||||
|     renderPosition(node) { | ||||
|       node = JSON.parse(JSON.stringify(node)) | ||||
|       let pos = {x: 0, y: 0} | ||||
|       if (node?.x) { | ||||
|         pos.x = Math.max(this.graph?.getWidth() / 3 * 2, node.x) + 100 + Math.random() * 50 | ||||
|         pos.y = node.y + Math.random() * 100 - 50 | ||||
|       } else if (node.value == '应用角色') { | ||||
|         pos.x = this.graph?.getWidth() / 3 - 200 + Math.random() * 200 | ||||
|         pos.y = this.graph?.getHeight() / 2 - 100 + Math.random() * 200 | ||||
|       } else if (node.value == '应用') { | ||||
|         pos.x = this.graph?.getWidth() / 3 * 2 - 100 + Math.random() * 100 | ||||
|         pos.y = Math.random() * this.graph?.getHeight() | ||||
|       } else if (node.value == '用户') { | ||||
|         pos.x = Math.random() * 50 | ||||
|         pos.y = Math.random() * this.graph?.getHeight() | ||||
|       } else { | ||||
|         pos.x = this.graph?.getWidth() - 100 + Math.random() * 100 | ||||
|         pos.y = Math.random() * this.graph?.getHeight() | ||||
|       } | ||||
|       return pos | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.getAppRoles(this.app) | ||||
|     if (this.app?.id) { | ||||
|       this.getUsers(this.app.users, this.app.id) | ||||
|       this.getPermissions(this.app) | ||||
|     } | ||||
|   }, | ||||
|   mounted() { | ||||
|     this.$nextTick(() => { | ||||
|       this.initGraph() | ||||
|     }) | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .rightsGraph { | ||||
|   height: 100%; | ||||
|  | ||||
|   ::v-deep #RightGraph { | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     min-height: 500px; | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										211
									
								
								core/apps/AppSystemAccount/AppSystemAccount.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								core/apps/AppSystemAccount/AppSystemAccount.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | ||||
| <template> | ||||
|   <section class="AppSystemAccount"> | ||||
|     <ai-list> | ||||
|       <ai-title slot="title" title="账号管理" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-search-bar> | ||||
|           <template #left> | ||||
|             <el-button type="primary" icon="iconfont iconAdd" @click="dialog=true">添加</el-button> | ||||
| <!--            <el-button type="primary" :disabled="!ids.toString()" @click="batchAllot">功能分配</el-button>--> | ||||
|           </template> | ||||
|           <template #right> | ||||
|             <el-input size="small" placeholder="搜索姓名、手机号" v-model="search.condition" clearable | ||||
|                       @change="page.pageNum=1,getTableData()"/> | ||||
|           </template> | ||||
|         </ai-search-bar> | ||||
|         <ai-table :tableData="tableData" :total="page.total" :current.sync="page.pageNum" :size.sync="page.pageSize" | ||||
|                   @getList="getTableData" :col-configs="colConfigs" :dict="dict" | ||||
|                   @selection-change="v=>ids=v.map(e=>e.id)"> | ||||
|           <el-table-column slot="name" label="姓名" width="180px"> | ||||
|             <el-row type="flex" align="middle" slot-scope="{row}"> | ||||
|               <el-image class="avatar" :src="row.avatar" :preview-src-list="[row.avatar]"> | ||||
|                 <el-image slot="error" src="https://cdn.cunwuyun.cn/dvcp/h5/defaultAvatar.png" alt=""/> | ||||
|               </el-image> | ||||
|               <div>{{ row.name }}</div> | ||||
|             </el-row> | ||||
|           </el-table-column> | ||||
|           <el-table-column slot="options" align="center" label="操作" fixed="right" width="160px"> | ||||
|             <el-row type="flex" justify="center" align="middle" slot-scope="{row}"> | ||||
|               <el-button type="text" @click="appAllot(row)">功能分配</el-button> | ||||
|               <el-button type="text" @click="handleDelete(row.id)">删除</el-button> | ||||
|             </el-row> | ||||
|           </el-table-column> | ||||
|         </ai-table> | ||||
|       </template> | ||||
|     </ai-list> | ||||
|     <!--添加账号、功能分配--> | ||||
|     <ai-dialog :title="dialogTitle" :visible.sync="dialog" width="600px" @open="initDialogData" | ||||
|                @onConfirm="updateAccount" @closed="dialogForm={}"> | ||||
|       <el-form ref="updateAccountForm" :model="dialogForm" :rules="rules" size="small" | ||||
|                label-width="120px"> | ||||
|         <el-form-item required label="姓名" prop="name"> | ||||
|           <el-input v-model.trim="dialogForm.name" placeholder="请输入..." clearable | ||||
|                     :maxLength="15"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item required label="手机号码" prop="phone"> | ||||
|           <el-input v-model.trim="dialogForm.phone" placeholder="请输入..." clearable | ||||
|                     :maxLength="11" :disabled="isEdit"/> | ||||
|         </el-form-item> | ||||
|         <el-form-item required label="角色" prop="roleId"> | ||||
|           <el-select size="small" placeholder="请选择角色" :value="dialogForm.roleId" filterable | ||||
|                      v-model="dialogForm.roleId" clearable> | ||||
|             <el-option v-for="(op,i) in accountRoles" :key="i" :label="op.name" :value="op.id"/> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="行政地区" prop="areaId"> | ||||
|           <ai-area-get v-model="dialogForm.areaId" :instance="instance" @select="handleAreaSelect"/> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </ai-dialog> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppSystemAccount", | ||||
|   label: "账号管理", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|     dict: Object, | ||||
|     permissions: Function | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     cascaderProps() { | ||||
|       return { | ||||
|         value: 'id', | ||||
|         checkStrictly: true, | ||||
|         emitPath: false | ||||
|       } | ||||
|     }, | ||||
|     isEdit() { | ||||
|       return !!this.dialogForm.id | ||||
|     }, | ||||
|     dialogTitle() { | ||||
|       return this.isEdit ? '功能分配' : '添加账号' | ||||
|     }, | ||||
|     colConfigs() { | ||||
|       return [ | ||||
|         // {type: 'selection', align: 'center'}, | ||||
|         {label: "姓名", slot: "name"}, | ||||
|         {label: "联系方式", prop: "phone", align: 'center'}, | ||||
|         {label: "角色", prop: "roleName", align: 'center'}, | ||||
|         {label: "地区", prop: "areaName"}, | ||||
|         {slot: "options"} | ||||
|       ] | ||||
|     }, | ||||
|     rules() { | ||||
|       return { | ||||
|         name: [{required: true, message: "请填写姓名"}], | ||||
|         // organizationId: [{required: true, message: "请选择党组织"}], | ||||
|         // unitId: [{required: true, message: "请选择单位"}], | ||||
|         // areaId: [{required: true, message: '请选择地区', trigger: 'change'}], | ||||
|         roleId: [{required: true, message: "请选择角色"}], | ||||
|         phone: [{required: true, message: "请输入手机号码"}] | ||||
|       } | ||||
|     }, | ||||
|     disabledLevel() { | ||||
|       return this.user.info.areaList?.length || 0 | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       accountRoles: [], | ||||
|       page: {pageNum: 1, pageSize: 10, total: 0}, | ||||
|       dialog: false, | ||||
|       dialogForm: {}, | ||||
|       tableData: [], | ||||
|       search: {condition: ""}, | ||||
|       ids: [] | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     getTableData() { | ||||
|       this.instance.post("/admin/user/page", null, { | ||||
|         params: {...this.page, ...this.search} | ||||
|       }).then(res => { | ||||
|         if (res?.data) { | ||||
|           this.tableData = res.data?.records | ||||
|           this.page.total = res.data.total | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     initDialogData() { | ||||
|       //用于优化初始化数据 | ||||
|       this.getAccountRoles() | ||||
|     }, | ||||
|     getAccountRoles() { | ||||
|       this.accountRoles.length == 0 && this.instance.post("/admin/role/list-all").then(res => { | ||||
|         if (res?.data) { | ||||
|           this.accountRoles = res.data | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     batchAllot() { | ||||
|       this.dialog = true | ||||
|       this.dialogForm = {areaId: this.user.info.areaId, ids: this.ids} | ||||
|     }, | ||||
|     appAllot(row) { | ||||
|       this.dialog = true | ||||
|       this.dialogForm = JSON.parse(JSON.stringify({ | ||||
|         ...row, | ||||
|         areaId: row.areaId || this.user.info.areaId | ||||
|       })); | ||||
|     }, | ||||
|     // 修改 | ||||
|     updateAccount() { | ||||
|       this.$refs.updateAccountForm.validate(v => { | ||||
|         if (v) { | ||||
|           this.instance.post("/admin/user/addOrEdit", this.dialogForm).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.dialog = false; | ||||
|               this.$message.success("提交成功") | ||||
|               this.getTableData(); | ||||
|             } else { | ||||
|               this.$message.error(res?.msg) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     }, | ||||
|     handleDelete(ids) { | ||||
|       this.$confirm("是否要删除该账号?").then(() => { | ||||
|         this.instance.post("/admin/user/del", null, { | ||||
|           params: {ids} | ||||
|         }).then(res => { | ||||
|           if (res?.code == 0) { | ||||
|             this.getTableData(); | ||||
|             this.$message.success("删除成功!"); | ||||
|           } | ||||
|         }) | ||||
|       }).catch(() => 0) | ||||
|     }, | ||||
|     handleAreaSelect(v) { | ||||
|       this.dialogForm.areaName = v?.[0]?.label | ||||
|     } | ||||
|   }, | ||||
|   created() { | ||||
|     this.getTableData() | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppSystemAccount { | ||||
|   height: 100%; | ||||
|  | ||||
|   ::v-deep .avatar { | ||||
|     width: 40px; | ||||
|     height: 40px; | ||||
|     margin-right: 10px; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .el-form { | ||||
|     .el-cascader, .el-select { | ||||
|       width: 100%; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										197
									
								
								core/apps/AppUserInfo/AppUserInfo.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								core/apps/AppUserInfo/AppUserInfo.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,197 @@ | ||||
| <template> | ||||
|   <section class="AppUserInfo"> | ||||
|     <ai-detail> | ||||
|       <ai-title slot="title" title="个人中心" isShowBottomBorder/> | ||||
|       <template #content> | ||||
|         <ai-card title="个人资料"> | ||||
|           <template #right> | ||||
|             <span style="color:#999" v-text="'(如需修改基本信息,请联系管理员)'"/> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <el-row type="flex" justify="space-between"> | ||||
|               <ai-wrapper> | ||||
|                 <ai-info-item label="姓名"> | ||||
|                   <ai-open-data type="userName" :openid="user.info.name" /> | ||||
|                 </ai-info-item> | ||||
|                 <ai-info-item label="手机号码" :value="user.info.phone"/> | ||||
|                 <ai-info-item label="角色" :value="user.info.roleName"/> | ||||
|                 <ai-info-item label="部门"> | ||||
|                   <ai-open-data type="departmentName" :openid="user.info.departName" /> | ||||
|                 </ai-info-item> | ||||
|                 <ai-info-item label="职位" :value="user.info.position"/> | ||||
|               </ai-wrapper> | ||||
|               <ai-avatar :value="user.info.avatar" :editable="false"/> | ||||
|             </el-row> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="数据权限"> | ||||
|           <template #right> | ||||
|             <span style="color:#999" v-text="'(如需修改基本信息,请联系管理员)'"/> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <ai-wrapper> | ||||
|               <ai-info-item label="所属地区" :value="user.info.areaList.join('')" isLine/> | ||||
|               <ai-info-item label="所属党组织" :value="user.info.organizationName" isLine/> | ||||
|             </ai-wrapper> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|         <ai-card title="密码设置"> | ||||
|           <template #right> | ||||
|             <el-button type="text" @click="submitForm">保存</el-button> | ||||
|           </template> | ||||
|           <template #content> | ||||
|             <el-form :model="form" ref="ruleForm" :rules="rules" label-width="100px" size="small"> | ||||
|               <el-form-item label="绑定手机:" prop="phone"> | ||||
|                 <el-row type="flex" align="middle"> | ||||
|                   <span>{{ user.info.phone }}</span> | ||||
|                   <el-button type="primary" style="margin-left: 8px" @click="getCode(user.info.phone,true)" | ||||
|                              :disabled="codeBtn">获取验证码 | ||||
|                     <span v-if="num>0&&codeBtn" style="color:red;">({{ num }}s)</span> | ||||
|                   </el-button> | ||||
|                 </el-row> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="验证码:" prop="code"> | ||||
|                 <el-input v-model.trim="form.code" clearable placeholder="请输入短信验证码"/> | ||||
|                 <el-input style="position: fixed; bottom: -9999px"></el-input> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="新密码:" prop="pass"> | ||||
|                 <el-input type="password" auto-complete="new-password" v-model.trim="form.pass" clearable | ||||
|                           placeholder="8-16位,需要包含字母和数字及特殊字符(~!@#$%^&*,.?_-)" | ||||
|                           show-password/> | ||||
|               </el-form-item> | ||||
|               <el-form-item label="确认密码:" prop="checkPass"> | ||||
|                 <el-input type="password" auto-complete="new-password" placeholder="再次输入密码" | ||||
|                           v-model.trim="form.checkPass" clearable | ||||
|                           show-password/> | ||||
|               </el-form-item> | ||||
|             </el-form> | ||||
|           </template> | ||||
|         </ai-card> | ||||
|       </template> | ||||
|     </ai-detail> | ||||
|   </section> | ||||
| </template> | ||||
|  | ||||
| <script> | ||||
| import {mapMutations, mapState} from "vuex"; | ||||
|  | ||||
| export default { | ||||
|   name: "AppUserInfo", | ||||
|   label: "个人中心", | ||||
|   props: { | ||||
|     instance: Function, | ||||
|   }, | ||||
|   computed: { | ||||
|     ...mapState(['user']), | ||||
|     rules() { | ||||
|       const validatePass = (rule, value, callback) => { | ||||
|         const reg = /^(?=.*\d)(?=.*[a-zA-Z])(?=.*[~!@#$%^&*,.?_-])[\da-zA-Z~!@#$%^&*,.?_-]{8,16}$/; | ||||
|         if (!reg.test(value)) { | ||||
|           callback(new Error('数字和字母及特殊字符(~!@#$%^&*,.?_-)组合,长度8到16位')); | ||||
|         } else { | ||||
|           if (this.form.checkPass !== '') { | ||||
|             this.$refs.ruleForm.validateField('checkPass'); | ||||
|           } | ||||
|           callback(); | ||||
|         } | ||||
|       } | ||||
|       return { | ||||
|         currentPass: [ | ||||
|           {required: true, message: '请填写当前密码', trigger: 'blur'} | ||||
|         ], | ||||
|         code: [ | ||||
|           {required: true, message: '请填写验证码', trigger: 'blur'} | ||||
|         ], | ||||
|         pass: [ | ||||
|           {validator: validatePass, trigger: 'blur', required: true} | ||||
|         ], | ||||
|         checkPass: [ | ||||
|           { | ||||
|             validator: (r, v, cb) => v ? v != this.form.pass ? cb('两次输入密码不一致') : cb() : cb('请再次输入密码'), | ||||
|             trigger: 'blur', | ||||
|             required: true | ||||
|           } | ||||
|         ], | ||||
|       } | ||||
|     } | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       form: {}, | ||||
|       timer: null, | ||||
|       codeBtn: false, | ||||
|       num: 60, | ||||
|     } | ||||
|   }, | ||||
|   methods: { | ||||
|     ...mapMutations(['SignOut']), | ||||
|     getCode(phone, flag) { | ||||
|       const TEL_REGEXP = /^1([38][0-9]|4[579]|5[0-3,5-9]|6[6]|7[0135678]|9[89])\d{8}$/; | ||||
|       if (TEL_REGEXP.test(phone)) { | ||||
|         this.instance.post(`/admin/user/checkPhone`, null, { | ||||
|           params: {phone, flag} | ||||
|         }).then(res => { | ||||
|           if (res.code == 0) { | ||||
|             this.$message.success("请查看手机短信!"); | ||||
|             this.timer = setInterval(() => { | ||||
|               if (this.num > 0) { | ||||
|                 this.codeBtn = true; | ||||
|                 this.num--; | ||||
|               } else if (this.num == 0) { | ||||
|                 this.codeBtn = false; | ||||
|                 this.num = 60; | ||||
|                 clearInterval(this.timer); | ||||
|  | ||||
|               } | ||||
|             }, 1000) | ||||
|  | ||||
|           } else { | ||||
|             this.$message.error("验证码发送失败!"); | ||||
|           } | ||||
|  | ||||
|         }) | ||||
|  | ||||
|  | ||||
|       } else { | ||||
|         this.$message.error("手机号格式错误!"); | ||||
|       } | ||||
|  | ||||
|     }, | ||||
|     submitForm() { | ||||
|       this.$refs.ruleForm.validate(v => { | ||||
|         if (v) { | ||||
|           let {pass: newPwd, code} = this.form | ||||
|           this.instance.post(`/admin/user/update-pwd`, null, { | ||||
|             params: { | ||||
|               phone: this.user.info.phone, | ||||
|               newPwd, code | ||||
|             } | ||||
|           }).then(res => { | ||||
|             if (res?.code == 0) { | ||||
|               this.$confirm("村微提醒您,更换密码成功!", { | ||||
|                 showCancelButton: false | ||||
|               }).then(() => this.SignOut()).catch(() => 0) | ||||
|             } | ||||
|           }) | ||||
|         } | ||||
|       }) | ||||
|     } | ||||
|   } | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .AppUserInfo { | ||||
|   height: 100%; | ||||
|  | ||||
|   ::v-deep .ai-list__content--wrapper { | ||||
|     flex-direction: column; | ||||
|   } | ||||
|  | ||||
|   ::v-deep .el-input__inner { | ||||
|  | ||||
|     -webkit-text-security: disc !important; | ||||
|  | ||||
|   } | ||||
| } | ||||
| </style> | ||||
		Reference in New Issue
	
	Block a user