Commit fca2c200 authored by 陈浩玮's avatar 陈浩玮

系统管理

parent b49612b9
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
"homepage": "https://iczer.github.io/vue-antd-admin", "homepage": "https://iczer.github.io/vue-antd-admin",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "vue-cli-service serve",
"serve": "vue-cli-service serve", "serve": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"lint": "vue-cli-service lint", "lint": "vue-cli-service lint",
...@@ -86,4 +87,4 @@ ...@@ -86,4 +87,4 @@
"git add" "git add"
] ]
} }
} }
\ No newline at end of file
import { delReq, getReq, postReq, putReq } from '@/utils'; import { delReq, getReq, postReq, putReq } from '@/utils';
function getJobsApi(data) {
return getReq('/api/v1/jobs', data);
}
function addJobsApi(data) { function addJobsApi(data) {
return postReq('/api/v1/jobs', data); return postReq('/api/v1/jobs', data);
} }
...@@ -8,12 +12,28 @@ function updateJobsApi(data) { ...@@ -8,12 +12,28 @@ function updateJobsApi(data) {
return putReq('/api/v1/jobs', data); return putReq('/api/v1/jobs', data);
} }
function getOrganizationList(data) { function getOrganizationListApi(data) {
return getReq('/api/v1/organizations', data); return getReq('/api/v1/organizations', data);
} }
function getOrganizationDetailsApi(id) {
return getReq(`/api/v1/organizations/${id}`);
}
function addOrgApi(data) {
return postReq('/api/v1/organizations', data);
}
function updateOrgApi(data) {
return putReq('/api/v1/organizations', data);
}
export default { export default {
addJobs: addJobsApi, addJobs: addJobsApi,
updateJobs: updateJobsApi, updateJobs: updateJobsApi,
getOrganizationList: getOrganizationList, getOrganizationList: getOrganizationListApi,
getJobs: getJobsApi,
getOrganizationDetails: getOrganizationDetailsApi,
addOrg: addOrgApi,
updateOrg: updateOrgApi,
}; };
<template>
<div class="components-input-demo-presuffix">
<a-input :placeholder="placeholder" :value="cron" @input="handleinput">
<a-icon slot="prefix" type="schedule" title="corn控件" @click="openModal" />
<a-icon v-if="cron" slot="suffix" type="close-circle" @click="handleEmpty" title="清空" />
</a-input>
<AntCron ref="innerVueCron" :data="afterCron" @ok="handleOK"></AntCron>
</div>
</template>
<script>
import AntCron from "./AntCron";
import { replaceWeekName } from "./validator";
export default {
name: "ACron",
components: {
AntCron
},
props: {
value: {
required: false,
type: String,
default: ""
},
placeholder: {
required: false,
type: String,
default: ""
}
},
data() {
return {
cron: this.value,
afterCron: ""
};
},
watch: {
value(val) {
this.cron = val;
},
cron(val) {
console.log(replaceWeekName(val));
this.afterCron = replaceWeekName(val);
console.log(val);
this.$emit("input", val);
}
},
methods: {
openModal() {
this.$refs.innerVueCron.show();
},
handleOK(val) {
this.cron = val;
this.$emit("change", this.cron);
},
handleinput(evt) {
this.cron = evt.target.value;
if (this.cron !== "") {
this.$emit("change", this.cron);
} else {
this.$emit("change", undefined);
}
},
handleEmpty() {
this.handleOK("");
}
},
model: {
prop: "value",
event: "change"
}
};
</script>
<style scoped>
.components-input-demo-presuffix .anticon-close-circle {
cursor: pointer;
color: #ccc;
transition: color 0.3s;
font-size: 12px;
}
.components-input-demo-presuffix .anticon-close-circle:hover {
color: #f5222d;
}
.components-input-demo-presuffix .anticon-close-circle:active {
color: #666;
}
</style>
\ No newline at end of file
This diff is collapsed.
import ACron from './ACron'
export default ACron
\ No newline at end of file
// import CronParser from 'cron-parser'
export const WEEK_MAP_EN = {
'sun': '0',
'mon': '1',
'tue': '2',
'wed': '3',
'thu': '4',
'fri': '5',
'sat': '6',
}
export const replaceWeekName = (c) => {
if (c) {
c = c.toLowerCase()
Object.keys(WEEK_MAP_EN).forEach(k => {
c = c.replace(new RegExp(k, 'g'), WEEK_MAP_EN[k])
})
c = c.replace(new RegExp('7', 'g'), '0')
}
return c
}
<template>
<a-select
:value="value"
style="width: 100%"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
placeholder="Please select"
allow-clear
:options="data"
v-on="$listeners"
v-bind="$attrs"
/>
</template>
<script>
import { formatObj } from '@/utils';
export default {
model: {
prop: 'value',
event: 'change',
},
props: {
value: [String, Number, Array],
request: Function,
},
data() {
return {
data: [],
};
},
async mounted() {
if (this.request) {
const newArr = await this.request();
this.data = await formatObj(newArr, { value: 'jobId', label: 'jobName', key: 'jobId' });
}
},
};
</script>
...@@ -24,13 +24,22 @@ ...@@ -24,13 +24,22 @@
<a-button :type="btn.primary" :key="btn.text">{{ btn.text }}</a-button> <a-button :type="btn.primary" :key="btn.text">{{ btn.text }}</a-button>
</template> </template>
</a-space> </a-space>
<div class="tw-mb-2">
<a-alert
v-if="selectedRowKeys.length"
:message="`已选择${selectedRowKeys.length}项`"
type="success"
closable
:after-close="handleClose"
/>
</div>
<a-table <a-table
:data-source="data" :data-source="data"
:loading="loading" :loading="loading"
v-bind="$attrs" v-bind="$attrs"
:pagination="pagination" :pagination="pagination"
@change="pageChange" @change="pageChange"
:row-selection="isRowSelection"
> >
<slot /> <slot />
</a-table> </a-table>
...@@ -85,6 +94,7 @@ export default { ...@@ -85,6 +94,7 @@ export default {
}, },
formatData: Function, formatData: Function,
noPage: Boolean, noPage: Boolean,
rowSelection: Object, // radio OR checkbox
}, },
data() { data() {
return { return {
...@@ -114,6 +124,8 @@ export default { ...@@ -114,6 +124,8 @@ export default {
wrapperCol: { span: 20 }, wrapperCol: { span: 20 },
}, },
total: 0, total: 0,
selectedRowKeys: [],
selectedRows: [],
}; };
}, },
watch: { watch: {
...@@ -135,6 +147,15 @@ export default { ...@@ -135,6 +147,15 @@ export default {
total: this.total, total: this.total,
}; };
}, },
isRowSelection() {
return this.rowSelection
? {
...this.rowSelection,
selectedRowKeys: this.selectedRowKeys,
onChange: this.onSelectChange,
}
: undefined;
},
}, },
methods: { methods: {
...@@ -202,6 +223,21 @@ export default { ...@@ -202,6 +223,21 @@ export default {
this.initQuery.pageNum = page.current; this.initQuery.pageNum = page.current;
this.getData(); this.getData();
}, },
onSelectChange(selectedRowKeys, selectedRows) {
this.selectedRowKeys = selectedRowKeys;
this.selectedRows = selectedRows;
},
handleClose() {
this.selectedRowKeys = [];
this.selectedRows = [];
},
getSelectedRowKeys() {
return this.selectedRowKeys;
},
getTableData() {
return this.data;
},
}, },
}; };
</script> </script>
......
<template> <template>
<div> <a-tree-select
<a-tree-select :value="value"
:value="value" show-search
show-search style="width: 100%"
style="width: 100%" :dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }" placeholder="Please select"
placeholder="Please select" :tree-data="treeData"
:tree-data="treeData" allow-clear
allow-clear tree-default-expand-all
tree-default-expand-all :replaceFields="replaceFields"
:replaceFields="replaceFields" v-on="$listeners"
v-on="$listeners" />
>
</a-tree-select>
</div>
</template> </template>
<script> <script>
...@@ -25,7 +22,7 @@ export default { ...@@ -25,7 +22,7 @@ export default {
event: 'change', event: 'change',
}, },
props: { props: {
value: [Object, Array], value: Number,
showMenu: Boolean, showMenu: Boolean,
}, },
data() { data() {
......
import Jobs from './Jobs';
export default Jobs;
<template>
<my-table
url="/api/v1/organizations"
rowKey="orgId"
:addBtn="addBtn"
ref="table"
:formatData="formatData"
noPage
:rowSelection="{ type: 'radio', getCheckboxProps, selections: true }"
>
<template #drawer>
<Form ref="form" :tableRef="$refs['table']" />
</template>
<a-table-column title="名称" data-index="orgName" />
<a-table-column title="组织分类" #default="row">
<a-tag color="#87d068" v-if="row.orgType === 'ORGANIZATION'"> {{ row.orgTypeName }}</a-tag>
<a-tag color="#108ee9" v-if="row.orgType === 'DEPARTMENT'"> {{ row.orgTypeName }}</a-tag>
</a-table-column>
<a-table-column title="说明" data-index="remark" />
<a-table-column title="操作">
<template #default="row">
<a @click="() => view(row, 1)">编辑</a>
<a-divider type="vertical" />
<PopconfirmDelete :url="`/api/v1/organizations/${row.orgId}`" :cb="refreshTable" />
</template>
</a-table-column>
</my-table>
</template>
<script>
import Form from './form.vue';
import PopconfirmDelete from '@/components/popconfirm_delete/index.vue';
import { arrayToTree } from '@/utils';
import api from '@/api/organization';
export default {
components: { Form, PopconfirmDelete },
data() {
return {
addBtn: { width: 600, onOk: () => this.$refs['form']?.submit(), openBefore: () => {} },
selectedRowKeys: [],
};
},
methods: {
refreshTable() {
this.$refs['table']?.getData();
},
async view(data, type) {
this.$refs['table']?.show({ type });
const detailData = await api.getOrganizationDetails(data.orgId);
this.$nextTick(() => {
this.$refs['form'].setData({ ...detailData }, type);
});
},
formatData(data) {
const newArr = new arrayToTree({
data: data,
rootValue: 0,
parentKey: 'parentOrgId',
key: 'orgId',
}).treeData;
return newArr;
},
getCheckboxProps(record) {
return {
props: {
...record,
disabled: record.orgType === 'DEPARTMENT',
},
};
},
},
};
</script>
\ No newline at end of file
<template>
<a-form-model layout="vertical" :model="form" :rules="rules" ref="DrawerForm">
<a-form-model-item label="组织分类" prop="orgType">
<a-select v-model="form.orgType" :disabled="isView">
<a-select-option value="ORGANIZATION"> 组织 </a-select-option>
<a-select-option value="DEPARTMENT"> 部门 </a-select-option>
</a-select>
</a-form-model-item>
<a-form-model-item label="名称" prop="orgName">
<a-input v-model="form.orgName" :disabled="isView" />
</a-form-model-item>
<a-form-model-item label="上级组织名称" prop="parentOrgId">
<a-select v-model="form.parentOrgId" disabled :options="options" />
</a-form-model-item>
<a-form-model-item v-if="form.orgType === 'DEPARTMENT'" label="岗位" prop="orgJobs">
<MySelect :request="Api.getJobs" v-model="form.orgJobs" mode="multiple" :disabled="isView" />
</a-form-model-item>
<a-form-model-item label="显示顺序" prop="viewIndex">
<a-input v-model="form.viewIndex" :disabled="isView" />
</a-form-model-item>
<a-form-model-item label="描述" prop="remark">
<a-textarea v-model="form.remark" :disabled="isView" :rows="4" />
</a-form-model-item>
</a-form-model>
</template>
<script>
import Api from '@/api/organization';
import FormMixin from '@/components/FormMixin';
import MySelect from '@/components/MySelect/index.vue';
export default {
mixins: [FormMixin],
components: { MySelect },
props: {
tableRef: Object,
},
created() {
const dataArr = this.tableRef
?.getTableData()
.map((i) => ({ value: i.orgId, label: i.orgName, key: i.orgId }));
const parentOrgId = this.tableRef?.getSelectedRowKeys()[0];
this.options = [...this.options, ...dataArr];
this.form.parentOrgId = parentOrgId || 0;
},
data() {
return {
options: [{ value: 0, label: '组织更目录', key: 0 }],
form: {
parentOrgId: 0,
},
rules: {
orgName: [{ required: true, message: 'Please select Activity zone', trigger: 'change' }],
orgType: [{ required: true, message: 'Please select Activity zone', trigger: 'change' }],
viewIndex: [{ required: true, message: 'Please select Activity zone', trigger: 'change' }],
},
Api,
};
},
methods: {
add() {
return Api.addOrg({ ...this.form });
},
edit() {
return Api.updateOrg({ ...this.form });
},
},
};
</script>
import User from './User';
export default User;
...@@ -79,21 +79,22 @@ const options = { ...@@ -79,21 +79,22 @@ const options = {
path: 'job_management', path: 'job_management',
name: '岗位管理', name: '岗位管理',
component: () => component: () =>
import('@/pages/system/view/organization/jobsmanagement'), import('@/pages/system/view/organization/jobsmanagement/Jobs.vue'),
}, },
{ {
path: 'user_management', path: 'user_management',
name: '用户管理', name: '用户管理',
component: () => component: () =>
import('@/pages/system/view/organization/usermanagement'), import('@/pages/system/view/organization/usermanagement/User.vue'),
},
{
path: 'org_management',
name: '机构管理',
component: () =>
import('@/pages/system/view/organization/orgmanagement/Org.vue'),
}, },
], ],
}, },
{
path: 'user_management',
name: '用户管理',
component: () => import('@/pages/system/view/user'),
},
{ {
path: 'role_management', path: 'role_management',
name: '角色管理', name: '角色管理',
......
...@@ -66,6 +66,7 @@ export function arrayToTree(options) { ...@@ -66,6 +66,7 @@ export function arrayToTree(options) {
const THISDATA = JSON.parse(JSON.stringify(this.data)); const THISDATA = JSON.parse(JSON.stringify(this.data));
for (let i = 0; i < this.data.length; i++) { for (let i = 0; i < this.data.length; i++) {
let currentElement = this.data[i]; let currentElement = this.data[i];
currentElement.selectable = currentElement.orgType === 'DEPARTMENT';
let tempCurrentElementParent = this.indexStorage[currentElement[this.parentKey]]; // 临时变量里面的当前元素的父元素 let tempCurrentElementParent = this.indexStorage[currentElement[this.parentKey]]; // 临时变量里面的当前元素的父元素
if (tempCurrentElementParent) { if (tempCurrentElementParent) {
// 如果存在父元素 // 如果存在父元素
...@@ -88,3 +89,16 @@ export function arrayToTree(options) { ...@@ -88,3 +89,16 @@ export function arrayToTree(options) {
this.toTree(); this.toTree();
return this; return this;
} }
// select组件 数据格式化
export const formatObj = async (ArrObj, obj) => {
const newData = [...ArrObj];
const newDataOne = await newData.map(i => {
const newObj = { ...i };
Object.keys(obj).forEach(key => {
newObj[key] = i[obj[key]];
});
return newObj;
});
return newDataOne;
};
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment