Commit 8368ce32 authored by 张俊's avatar 张俊
parents 98028892 3fbbf1d9
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<template>
<div
class="gap-title"
:class="{
'has-line': hasLine,
}">
<span>{{ title }}</span>
</div>
</template>
<script>
export default {
props: {
title: {
type: String,
default: "",
},
hasLine: {
type: Boolean,
default: false,
},
},
components: {},
data() {
return {};
},
watch: {},
computed: {},
created() {},
mounted() {},
methods: {},
};
</script>
<style scoped>
.gap-title {
height: 18px;
font-size: 18px;
font-weight: 600;
color: #202531;
display: flex;
align-items: center;
}
.gap-title::before {
content: "";
display: inline-block;
margin-right: 8px;
width: 4px;
height: 16px;
background-color: #2b4695;
border-radius: 2px;
}
.gap-title.has-line::after {
content: "";
display: inline-block;
flex: 1;
border-top: 1px dashed #dadee7;
margin-left: 8px;
}
</style>
<template>
<el-button size="default" @click="goBack">返回</el-button>
</template>
<script setup>
import { useRouter } from "vue-router";
const router = useRouter();
const props = defineProps({
href: {
type: Object,
default: null,
},
});
const goBack = () => {
if (props.href) {
router.push(props.href);
} else {
router.go(-1);
}
};
</script>
<style lang="scss" scoped></style>
// 通知方式
export const METHODS = {
1: '钉钉',
2: '短信'
}
// 可以新增的数量
export const ADD_NUM = 5
\ No newline at end of file
<template>
<el-form
:model="state.form"
ref="form"
:rules="state.rules"
label-width="80px"
:disabled="disabled"
>
<el-form-item label="通知方式" prop="method">
<el-checkbox-group v-model="state.form.method">
<el-checkbox v-for="(value, key) in METHODS" :key="key" :label="key">
{{ value }}
</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="" prop="lists" class="user-table bg-scroll">
<el-table :data="state.form.lists" stripe border>
<el-table-column type="index" label="序号" width="60px">
</el-table-column>
<el-table-column
v-for="header in headers"
:prop="header.prop"
:key="header.prop"
:label="header.label"
:width="header.width"
>
<template #default="{ $index }">
<div v-if="header.prop == 'user_id'">
<el-select
v-model="state.form.lists[$index].user_id"
placeholder="请选择"
filterable
@change="($event) => chooseUser($event, $index)"
>
<el-option
v-for="item in userOptions($index)"
:key="item.user_id"
:label="item.user_id"
:value="item.user_id"
>
</el-option>
</el-select>
</div>
<div v-else>
<el-input
v-model="state.form.lists[$index][header.prop]"
placeholder="请输入"
></el-input>
</div>
</template>
</el-table-column>
<el-table-column prop="" label="操作" width="125px">
<template #default="{ $index }">
<div class="table-operation">
<el-button
link
type="primary"
@click="Add($index)"
:disabled="
userLists.length <= state.form.lists.length ||
addTrue >= ADD_NUM
"
>
新增
</el-button>
<span class="line"></span>
<el-button
link
type="primary"
@click="Remove($index)"
:disabled="state.form.lists.length == 1"
>
删除
</el-button>
</div>
</template>
</el-table-column>
</el-table>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, watch, ref, computed } from "vue";
import { METHODS, ADD_NUM } from "./env";
const props = defineProps({
disabled: {
type: Boolean,
default: false,
},
});
const form = ref(null);
// 表单数据
const state = reactive({
form: {
method: [],
lists: [],
},
rules: {
method: [
{
type: "array",
required: true,
message: "请至少选择一个通知方式",
trigger: "change",
},
],
},
});
// 已选中的用户id列表
const selectedUser = computed(() => {
return state.form.lists.map((e) => e.user_id) || [];
});
// 表头
const headers = [
{
prop: "user_id",
label: "账号",
},
{
prop: "user_name",
label: "姓名",
},
{
prop: "phone",
label: "联系方式",
},
];
// 立即下发
const Submit = (cb = null) => {
form.value.validate((valid) => {
if (valid) {
console.log(state.form);
cb && cb();
} else {
return false;
}
});
};
// 已经新增的数量记录
const addTrue = ref(0);
// 获取远程用户列表
const userLists = ref([]);
const getUserLists = () => {
userLists.value = [
{
user_id: 1,
user_name: 11,
phone: 13000000001,
},
{
user_id: 2,
user_name: 22,
phone: 13000000002,
},
{
user_id: 3,
user_name: 33,
phone: 13000000003,
},
{
user_id: 4,
user_name: 44,
phone: 13000000004,
},
{
user_id: 5,
user_name: 55,
phone: 13000000005,
},
{
user_id: 6,
user_name: 66,
phone: 13000000006,
},
{
user_id: 7,
user_name: 77,
phone: 13000000007,
},
];
};
// 格式化处理用户下拉列表,去除已经选择的用户,并将当前选中的也显示到列表中
const userOptions = computed(() => {
return (index) => {
let user_id = state.form.lists[index].user_id;
let s_u_ids = [...selectedUser.value];
if (user_id) {
let i = s_u_ids.findIndex((e) => e == user_id);
s_u_ids.splice(i, 1);
}
return userLists.value.filter((e) => !s_u_ids.includes(e.user_id));
};
});
// 表格新增
const Add = (index) => {
// 新增上限
if (addTrue.value >= ADD_NUM) return;
state.form.lists.splice(index + 1, 0, {
user_id: "",
user_name: "",
phone: "",
});
addTrue.value += 1;
};
// 表格删除
const Remove = (index) => {
state.form.lists.splice(index, 1);
addTrue.value -= 1;
};
// 初始化加载基础数据和获取用户下拉列表
const init = (history) => {
getUserLists();
state.form.method = history?.method || [];
state.form.lists = history?.lists || [];
// 如果没有默认下发用户则创建一条空数据
if (state.form.lists.length == 0) {
Add(0);
}
setTimeout(() => {
form.value.clearValidate();
});
};
// 选择用户操作
const chooseUser = ($event, index) => {
let user = userLists.value.find(
(e) => e.user_id == state.form.lists[index].user_id
);
state.form.lists[index].user_name = user.user_name;
state.form.lists[index].phone = user.phone;
};
defineExpose({
Submit,
form,
init,
});
watch(
() => state.form.lists,
(n) => {
console.log(n);
},
{ deep: true }
);
</script>
<style lang="scss" scoped>
.table-operation {
display: flex;
align-items: center;
.line {
width: 1px;
height: 14px;
background-color: #c1c7d7;
margin: 0 16px;
display: inline-block;
}
}
.user-table {
max-height: 345px;
:deep(.el-form-item__content) {
margin-left: 0 !important;
.el-table thead th {
background-color: #f5f6f9;
}
}
}
</style>
<template>
<el-dialog
v-model="show"
width="804px"
top="10vh"
:close-on-click-modal="false"
:before-close="beforeClose"
>
<template #header>
<GapTitle title="手动下发"></GapTitle>
</template>
<div class="manual-distribution">
<Form ref="form" />
</div>
<template #footer>
<el-button size="default" @click="Close">关闭</el-button>
<el-button type="primary" size="default" @click="Submit">
立即下发
</el-button>
</template>
</el-dialog>
</template>
<script setup>
import GapTitle from "@/components/gap-title.vue";
import Form from "./form.vue";
import { computed, nextTick, ref, watch } from "vue";
const props = defineProps({
// 显示隐藏开关
visible: {
type: Boolean,
default: false,
},
// 关闭前调用
beforeClose: {
type: Function,
default: null,
},
// 默认数据展示
/*
{
methd:['1','2'],
lists:[
{
user_id:'1111',
user_name:'11111111',
phone:'13011111111'
}
]
}
*/
history: {
type: Object,
default: null,
},
});
const emits = defineEmits(["update:visible"]);
const form = ref(null);
// 关闭调用
const Close = async () => {
form.value.form.resetFields();
emits("update:visible", false);
};
const show = computed({
get() {
return props.visible;
},
set() {
Close();
},
});
// 监听是否开启弹窗,并将已经填写的数据配置
watch(
() => show.value,
async (n) => {
if (!n) return;
await nextTick();
form.value.init(props.history);
},
{
immediate: true,
}
);
// 关闭前调用
const beforeClose = () => {
props.beforeClose && props.beforeClose();
Close();
};
// 立即下发
const Submit = () => {
form.value.Submit(() => {
Close();
});
};
</script>
<style lang="scss" scoped>
.manual-distribution {
text-align: left;
padding: 16px 16px 0;
}
</style>
export const STATUS_OBJ = {
success: "已恢复",
error: '未恢复'
}
export const PUSH_STATUS = {
success: '成功',
error: '失败'
}
\ No newline at end of file
<template>
<div class="warn-detail">
<div class="info">
<Info :labelData="labelData" :valueData="valueData" />
</div>
<div class="tabs">
<Tab :tabs="tabs" />
</div>
</div>
</template>
<script setup>
import Info from "./info.vue";
import Tab from "./tab.vue";
const props = defineProps({
labelData: {
type: Array,
default: () => [],
},
valueData: {
type: Object,
default: () => ({}),
},
tabs: {
type: Array,
default: () => [],
},
});
</script>
<style lang="scss" scoped>
.warn-detail {
max-width: 1072px;
width: 100%;
height: 100%;
.tabs{
margin-top: 30px;
}
}
</style>
<template>
<div>
<div class="info-list" v-for="(list, index) in labelData" :key="`warn-${index}`">
<div class="info-item" v-for="(item, i) in list" :key="`warn-${index}-${i}`">
<div class="label">
{{ item.label }}
</div>
<div class="value">
<div class="value-body">
<span v-if="item.prop == 'status'">
<slot name="status" :item="item" :valueData="valueData" v-if="slots.status" />
<span class="status-body" v-else>
<span class="status" :class="`status-${valueData.status}`"></span>
<span>{{ STATUS_OBJ[valueData[item.prop]] }}</span>
</span>
</span>
<span v-else>
<slot name="value" :item="item" :valueData="valueData" v-if="slots.value" />
<span v-else>{{ valueData[item.prop] }}</span>
</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useSlots, computed } from "vue";
import { STATUS_OBJ } from "./env.js";
const slots = useSlots();
const props = defineProps({
labelData: {
type: Array,
default: () => ({}),
},
valueData: {
type: Object,
default: () => ({}),
},
status_obj: {
type: Object,
default: null,
},
});
const status_obj = computed(() => {
return props.status_obj || STATUS_OBJ;
});
</script>
<style lang="scss" scoped>
.info-list {
display: flex;
align-items: center;
border-top: 1px solid #dadee7;
border-left: 1px solid #dadee7;
&:last-of-type {
border-bottom: 1px solid #dadee7;
}
.info-item {
display: flex;
align-items: center;
flex: 1;
height: 48px;
line-height: 46px;
font-size: 14px;
color: #404a62;
.label {
height: 100%;
width: 144px;
background-color: #f7f7f9;
border-right: 1px solid #dadee7;
padding: 0 16px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
}
.value {
height: 100%;
flex: 1;
border-right: 1px solid #dadee7;
position: relative;
.value-body {
padding: 0 16px;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
.status {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
$statusObj: (
success: #48ad97,
error: #d75138,
);
@each $status, $color in $statusObj {
&-#{$status} {
background-color: $color;
}
}
}
.status-body {
display: flex;
align-items: center;
}
}
}
}
}
</style>
<template>
<el-tabs v-model="activeTab" tab-position="top">
<el-tab-pane v-for="item in tabs" :key="item.prop" :label="item.label" :name="item.prop">
<bg-table
ref="listtable"
:headers="headers[item.prop]"
:rows="item.lists"
height="100%"
:isIndex="true"
:stripe="true">
<template #person="{ row }">
<span>{{ row.person.join(",") }}</span>
</template>
<template #status="{ row }">
<span v-if="item.prop == 'push'">
<span class="status" :class="`status-${row.status}`"></span>
<span>{{ STATUS_OBJ[row.status] }}</span>
</span>
<span v-else :class="`tab-${row.status}`">
{{ STATUS_OBJ[row.status] }}
</span>
</template>
</bg-table>
</el-tab-pane>
</el-tabs>
</template>
<script setup>
import { ref } from "vue";
import { STATUS_OBJ } from "./env.js";
const activeTab = ref("push");
const props = defineProps({
tabs: {
type: Array,
default: () => [],
},
});
const headers = {
push: [
{
prop: "method",
label: "通知方式",
},
{
prop: "person",
label: "接收人员",
},
{
prop: "push_time",
label: "推送时间",
},
{
prop: "push_type",
label: "推送类型",
},
{
prop: "status",
label: "状态",
},
],
dispose: [
{
prop: "status",
label: "状态",
},
{
prop: "feedback",
label: "处置反馈",
},
{
prop: "feedback_time",
label: "处置反馈",
},
{
prop: "feedback_person",
label: "反馈人员",
},
],
};
</script>
<style lang="scss" scoped>
$statusObj: (
success: #48ad97,
error: #d75138,
);
.tab {
@each $status, $color in $statusObj {
&-#{$status} {
color: $color;
}
}
}
.status {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 50%;
margin-right: 8px;
@each $status, $color in $statusObj {
&-#{$status} {
background-color: $color;
}
}
}
</style>
<template>
<el-form
:label-position="'right'"
label-width="120px"
:model="directoryForm"
:rules="directoryFormRules"
ref="directoryRef"
style="max-width: 80%">
<el-form-item label="上级目录" prop="p_organization_id" v-if="formType === 'create'">
<el-tree-select
v-model="directoryForm.p_organization_id"
:data="orgData"
:props="treeProps"
:render-after-expand="false"
:check-strictly="true"
:disabled="directoryForm.level"
style="width: 80%" />
<el-form-item label="" style="padding-left: 16px">
<el-checkbox v-model="directoryForm.level" @change="isTop" label="顶级" />
</el-form-item>
</el-form-item>
<el-form-item label="名称" prop="name">
<el-input v-model="directoryForm.name" />
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref, onMounted, onBeforeMount, nextTick } from "vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const props = defineProps({
formType: {
type: String,
default: "create", //false 新增 true 编辑
},
});
const directoryForm = reactive({
p_organization_id: "",
name: "",
level: false,
});
const directoryFormRules = reactive({
p_organization_id: [{ required: true, message: "请输入上级目录", trigger: "blur" }],
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
});
const directoryRef = ref(null);
const emit = defineEmits(["action"]);
const submitForm = async () => {
if (!directoryRef) return;
await directoryRef.value.validate((valid, fields) => {
if (valid) {
emit("action", directoryForm);
} else {
emit("action", null);
}
});
};
const clearForm = () => {
if (!directoryRef) return;
directoryRef.value.resetFields();
};
const setForm = (data) => {
Object.assign(directoryForm, data);
};
const orgData = ref([]);
const treeProps = {
label: "name",
children: "Child",
value: "organization_id",
disabled: "disabled",
};
const getOrgTree = () => {
axios.get(`/apaas/system/v5/org/tree`).then((res) => {
if (res.data.code == 200) {
const orgDataTemp = res.data.data || [];
orgDataTemp.shift();
orgData.value = filterOrg(orgDataTemp);
} else {
ElMessage.error(res.data.data);
}
});
};
const filterOrg = (data) => {
if (data.length > 0) {
data.forEach((item) => {
item.disabled = item.data_type === 0 ? false : true;
if (item.Child) {
filterOrg(item.Child);
} else {
return;
}
});
}
return data;
};
const isTop = (data) => {
directoryForm.p_organization_id = data ? " " : ""; //空格绕过表单非空校验
};
onBeforeMount(() => {
getOrgTree();
});
onMounted(() => {});
defineExpose({ submitForm, clearForm, setForm });
</script>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="page_content apaas_scroll">
<div class="info_row">
<div class="title">
<div><span class="icon_box"></span> 基础信息</div>
<div class="dashed_line"></div>
</div>
<div class="info_content">
<div class="info_box">
<bg-info :data="baseInfo"></bg-info>
</div>
<div class="org_file info_box">
<div class="pl-1">组织附件</div>
<div>
<div class="file_item pl-1" v-for="(item, index) in fileList" :key="'file_' + index">
<span><bg-icon style="font-size: 24px" :icon="'#bg-ic-' + formatFile(item)"></bg-icon></span>
<span class="file_name">{{ downloadFileFormatNew(item) }}</span>
<span class="fr download_btn">
<el-button type="primary" @click="downloadFile(item)"
><bg-icon icon="#bg-ic-to-bottom"></bg-icon>下载</el-button
>
</span>
</div>
</div>
</div>
</div>
</div>
<div>
<div class="title">
<div><span class="icon_box"></span> 业务系统信息</div>
<div class="dashed_line"></div>
</div>
<div class="info_content info_box">
<bg-table ref="bgTable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true"> </bg-table>
<div class="pagination_box">
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, computed, onBeforeMount } from "vue";
import { useRoute } from "vue-router";
import { downloadFileFormatNew } from "@/services/helper";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const route = useRoute();
const baseInfo = reactive([
{
name: "组织名称",
value: "",
nameWidth: 130,
},
{
name: "组织代码",
value: "",
nameWidth: 130,
},
{
name: "组织管理员数量",
value: "",
nameWidth: 130,
},
{
name: "业务系统数量",
value: "",
nameWidth: 130,
},
{
name: "组织描述",
value: "",
nameWidth: 130,
},
]);
const headers = reactive([
{
label: "业务系统名称",
prop: "business_name",
},
{
label: "是否启用",
prop: "state",
},
{
label: "业务系统账号",
prop: "system_account",
},
]);
const tableRows = ref([]);
const fileList = ref([]);
const filter = reactive({
page: 1,
limit: 10,
});
const tableTotal = ref(0);
const changeSize = (size) => {
filter.limit = size;
changePage(1);
};
const changePage = (page) => {
filter.page = page;
getSystemInfo();
};
const downloadFile = (data) => {
const a = document.createElement("a"); // 创建a标签
a.setAttribute("download", ""); // download属性
a.setAttribute("href", data); // href链接
a.click(); // 自执行点击事件
};
const formatFile = (url) => {
const file = url.split(".")[1];
let icon = "";
if ("docx".indexOf(file) !== -1) {
icon = "c-file-doc";
} else if ("xlsx".indexOf(file) !== -1) {
icon = "c-file-xlsx";
} else if ("pdf".indexOf(file) !== -1) {
icon = "c-file-pdf";
} else if ("txt".indexOf(file) !== -1) {
icon = "c-file-txt";
} else if ("zip".indexOf(file) !== -1) {
icon = "c-file-zip";
} else if ("jpg,png".indexOf(file) !== -1) {
icon = "c-file-image";
} else {
icon = "txt";
}
return icon;
};
const getDetail = () => {
axios
.get(`/apaas/system/v5/org/detail?organization_id=${route.query.id}&key_word=&state=&limit=&page=&data_type=1`)
.then((res) => {
if (res.data.code == 200) {
const detail = res.data.data.org_info;
baseInfo[0].value = detail.name;
baseInfo[1].value = detail.organization_code;
baseInfo[2].value = detail.org_admin_number;
baseInfo[3].value = detail.business_system_number;
baseInfo[4].value = detail.description;
fileList.value = detail.attachment ? detail.attachment.split(",") : [];
} else {
ElMessage.error(res.data.data);
}
});
};
const getSystemInfo = () => {
const params = { ...filter, organization_id: route.query.id };
axios.get(`/apaas/system/v5/org/business/msg`, { params }).then((res) => {
if (res.data.code == 200) {
tableRows.value = res.data.data || [];
tableTotal.value = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
onBeforeMount(() => {
if (route.query.id) {
getDetail();
getSystemInfo();
}
});
</script>
<style scoped>
.page_content {
padding: 15px;
overflow: auto;
font-size: 14px;
color: #202531;
}
.info_content {
/* display: flex; */
}
.org_file {
margin-top: 15px;
}
.file_item {
height: 48px;
line-height: 48px;
background-color: #fafafa;
border-radius: 4px;
font-size: 14px;
margin-bottom: 8px;
}
.file_name {
padding-left: 10px;
color: #202531;
}
.download_btn {
padding-right: 4px;
}
.info_row {
margin-bottom: 40px;
}
.img_box {
width: 120px\;;
}
.info_box {
width: 60%;
}
.title {
font-size: 18px;
color: #1a1a1a;
font-weight: bold;
margin-bottom: 10px;
display: flex;
align-items: center;
}
.dashed_line {
flex: 1;
height: 1px;
margin: 0 10px;
border-bottom: dashed 1px #dadee7;
}
.icon_box {
display: inline-block;
width: 4px;
height: 14px;
background-color: #3759be;
border-radius: 2px;
margin-right: 5px;
}
.pagination_box {
position: sticky;
position: -webkit-sticky;
margin-top: 16px;
bottom: -15px;
background-color: #fff;
z-index: 1024;
height: 40px;
line-height: 40px;
padding-top: 5px;
}
.bg-pagination {
margin-top: 0px;
}
.info_content :deep() .bg-table .empty_container {
height: 160px;
padding-top: 25px;
}
</style>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<!-- 角色管理新增 -->
<template>
<role-form></role-form>
</template>
<script setup>
import roleForm from "./role_form.vue";
</script>
This diff is collapsed.
<!-- 角色管理编辑 -->
<template>
<role-form></role-form>
</template>
<script setup>
import roleForm from "../add/role_form.vue";
</script>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<template>
<div class="feedback-detail">
<div class="detail-box" v-for="(detail, index) in detailInfo" :key="`feedback-detail-${index}`">
<Detail :info="detail" />
</div>
</div>
</template>
<script setup>
import Detail from "./detail.vue";
const props = defineProps({
detailInfo: {
type: Array,
default: () => [],
},
});
</script>
<style lang="scss" scoped>
.detail-box {
&:not(:first-child) {
margin-top: 16px;
}
}
</style>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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