初始化
This commit is contained in:
4
core/.npmignore
Normal file
4
core/.npmignore
Normal file
@@ -0,0 +1,4 @@
|
||||
apps/
|
||||
index.js
|
||||
*.map
|
||||
vcapps.import.js
|
||||
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>
|
||||
242
core/components/AiAddressBookMenu.vue
Normal file
242
core/components/AiAddressBookMenu.vue
Normal file
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<section class="AiAddressBookMenu">
|
||||
<div class="tabsPane">
|
||||
<h2 v-for="tab in tabs" :key="tab.name" :class="{tabActive:current==tab.name}"
|
||||
@click="handleTabClick(tab)">{{ tab.label }}</h2>
|
||||
</div>
|
||||
<div class="contentPane">
|
||||
<el-input :placeholder="`请输入${currentTab.label}名称`"
|
||||
size="small" v-model="search" suffix-icon="iconfont iconSearch"
|
||||
@change="handleSearchChange"/>
|
||||
<div v-if="isTagType" class="addressBook-left__tags">
|
||||
<div @click="selected=item" v-for="(item, index) in tags" :key="index"
|
||||
class="addressBook-left__tags--item"
|
||||
:class="{'addressBook-left__tags--item-active':selected.id == item.id}">
|
||||
<span>{{ item.tagname }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="AiAddressBookMenu-wrapper" v-else>
|
||||
<div class="AiAddressBookMenu-tree">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
node-key="id"
|
||||
:filter-node-method="handleTreeFilter"
|
||||
:props="defaultProps" :data="list"
|
||||
:default-expanded-keys="treeRoot"
|
||||
@current-change="v=>selected=v">
|
||||
<div class="tree-container" slot-scope="{ data }">
|
||||
<span>{{ data.name }}</span>
|
||||
</div>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 通讯录选择器
|
||||
*/
|
||||
export default {
|
||||
name: "AiAddressBookMenu",
|
||||
props: {
|
||||
instance: Function,
|
||||
},
|
||||
computed: {
|
||||
tabs() {
|
||||
return [
|
||||
{label: "组织架构", name: 0},
|
||||
{label: "标签", name: 1}
|
||||
]
|
||||
},
|
||||
currentTab() {
|
||||
return this.tabs.find(e => e.name == this.current) || {}
|
||||
},
|
||||
isTagType() {
|
||||
return this.current == 1
|
||||
},
|
||||
defaultProps() {
|
||||
return {
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}
|
||||
},
|
||||
tags() {
|
||||
return this.list?.filter(e => !this.search || e.tagname?.indexOf(this.search) > -1)
|
||||
},
|
||||
treeRoot() {
|
||||
let root = this.list?.[0]
|
||||
return root ? [root?.id] : []
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
current: 0,
|
||||
search: "",
|
||||
list: [],
|
||||
origin: [],
|
||||
selected: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
selected(v) {
|
||||
v && this.$emit('select', {...v, type: this.current})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTabClick(tab) {
|
||||
this.current = tab.name
|
||||
this.isTagType ? this.getTags() : this.getUnits()
|
||||
this.$emit('tabClick')
|
||||
},
|
||||
handleTreeFilter(v, data) {
|
||||
return data?.name?.indexOf(v) > -1
|
||||
},
|
||||
handleSearchChange(v) {
|
||||
if (this.isTagType) {
|
||||
} else {
|
||||
this.$refs.tree?.filter(v)
|
||||
}
|
||||
},
|
||||
handleTagClick(tag) {
|
||||
this.$emit('tag', tag)
|
||||
this.selected = tag
|
||||
},
|
||||
getUnits() {
|
||||
this.instance.post(`/app/wxcp/wxdepartment/listAll`).then(res => {
|
||||
if (res?.data) {
|
||||
this.list = res.data?.filter(e => !e.parentid)
|
||||
this.list.map(p => this.addChild(p, res.data, {parent: 'parentid'}))
|
||||
}
|
||||
})
|
||||
},
|
||||
getTags() {
|
||||
this.instance.post(`/app/wxcp/wxtag/listAll`).then(res => {
|
||||
if (res?.data) {
|
||||
this.origin = JSON.parse(JSON.stringify(res.data))
|
||||
this.list = res?.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getUnits()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.AiAddressBookMenu {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #FAFAFB;
|
||||
.tabsPane {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
background: #ffffff;
|
||||
|
||||
h2 {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
line-height: 40px;
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
border-bottom: 2px solid transparent;
|
||||
|
||||
&.tabActive {
|
||||
color: #2266FF;
|
||||
border-bottom: 2px solid #2266FF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.AiAddressBookMenu-wrapper {
|
||||
height: calc(100% - 32px);
|
||||
.AiAddressBookMenu-tree {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.contentPane {
|
||||
height: calc(100% - 56px);
|
||||
margin: 8px;
|
||||
overflow: auto;
|
||||
|
||||
.addressBook-left__tags--item {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 8px 0 16px;
|
||||
color: #222222;
|
||||
|
||||
&.addressBook-left__tags--item-active, &:hover {
|
||||
background: #E8EFFF;
|
||||
color: #2266FF;
|
||||
|
||||
i, span {
|
||||
color: #2266FF;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
i {
|
||||
cursor: pointer;
|
||||
color: #8e9ebf;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
::v-deep .el-tree {
|
||||
width: 100%;
|
||||
margin-top: 4px;
|
||||
background: transparent;
|
||||
width: fit-content;
|
||||
min-width: 100%;
|
||||
|
||||
.el-tree-node__content {
|
||||
display: inline-flex;
|
||||
min-width: 100%;
|
||||
height: 32px;
|
||||
|
||||
&:hover {
|
||||
background: #E8EFFF;
|
||||
color: #222222;
|
||||
border-radius: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.is-current > .el-tree-node__content {
|
||||
background: #2266FF;
|
||||
|
||||
&:hover {
|
||||
background: #2266FF;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
45
core/index.js
Normal file
45
core/index.js
Normal file
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* 系统业务模块
|
||||
* @param Vue 外部接入Vue
|
||||
* @param params showList:打印加载的应用;apps:加载的应用文件名数组
|
||||
*/
|
||||
|
||||
const install = function (Vue, params) {
|
||||
if (install.installed) return Promise.resolve()
|
||||
else {
|
||||
// 遍历工作控件内的应用
|
||||
let apps = []
|
||||
let contexts = require.context('.', true, /\.(\/.+)\/App[^\/]+\.vue$/)
|
||||
if (contexts) {
|
||||
contexts.keys().map(e => {
|
||||
if (contexts(e).default) {
|
||||
if (params?.apps) {
|
||||
if (params?.apps.includes(contexts(e).default.name)) {
|
||||
apps.push(contexts(e).default)
|
||||
Vue.component(contexts(e).default.name, contexts(e).default)
|
||||
}
|
||||
} else {
|
||||
apps.push(contexts(e).default)
|
||||
Vue.component(contexts(e).default.name, contexts(e).default)
|
||||
}
|
||||
}
|
||||
})
|
||||
// apps.map(e=>{
|
||||
// console.log(e.name,e.label)
|
||||
// })
|
||||
!!params?.showList && console.log(apps.map(e => e.name))
|
||||
}
|
||||
return Promise.resolve(apps)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 判断是否是直接引入文件
|
||||
if (typeof window !== 'undefined' && window.Vue) {
|
||||
install(window.Vue)
|
||||
}
|
||||
|
||||
export default {
|
||||
// 导出的对象必须具有 install,才能被 Vue.use() 方法安装
|
||||
install
|
||||
}
|
||||
9
core/package.json
Normal file
9
core/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"name": "cw-core-webapps",
|
||||
"description": "系统业务模块",
|
||||
"version": "1.0.13",
|
||||
"main": "dist/cw-core-webapps.common.js",
|
||||
"publishConfig": {
|
||||
"registry": "http://192.168.1.87:4873/"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user