Commit 676c6858 authored by 李鹏 's avatar 李鹏

删除多余文件

parent 26f4c4e9
<!-- 角色管理 -->
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="form_content apaas_scroll_nor">
<el-form ref="refForm" label-position="left" :model="formData" :rules="rules" label-width="82px">
<div class="top_content">
<el-form-item label="规则名称" prop="ruleName">
<el-input placeholder="请输入" maxlength="100" v-model="formData.ruleName"></el-input>
</el-form-item>
<el-form-item label="规则类型" prop="ruleType">
<el-select placeholder="请选择" style="width: 100%" v-model="formData.ruleType">
<el-option label="IP" :value="1"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="规则" prop="ruleDetail">
<el-input
placeholder="请输入"
rows="5"
maxlength="300"
show-word-limit
type="textarea"
v-model="formData.ruleDetail" />
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>输入IP段,不可重复,例如:192.168.0.0/24</span><br />
<span>输入IP地址,例如192.168.0.1(支持ipv4地址,配置多个IP时使用回车符分隔)</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
<el-form-item class="switch-item" label="是否启用" prop="state" required>
<bg-switch :labels="['否', '是']" :values="[0, 1]" v-model="formData.state"></bg-switch>
</el-form-item>
</div>
<el-form-item class="userScope" label="用户范围" prop="userScope">
<el-button type="primary" @click="userMaintain"> 用户维护 </el-button>
<div class="table_content clearfix">
<bg-table ref="bgTable" :headers="detailHeaders" :rows="tableRows" height="550" :stripe="true">
<template v-slot:isAdmin="{ row }">
<span>{{ userTypeConfig[row.isAdmin] }}</span>
</template>
</bg-table>
<bg-pagination
:page="pageConfig.page"
:size="pageConfig.limit"
:total="pageConfig.total"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</el-form-item>
</el-form>
<el-dialog class="dialog_box_maintain" title="用户维护" v-model="dialogMaintain" width="1062px">
<div class="content_detail">
<div class="form_filter">
<div class="left">
<span class="sleceted_tip"
>已选择<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
<div class="right">
<el-select
placeholder="全部类型"
v-model="dialogFilterData.isAdmin"
style="width: 200px; margin-left: 16px">
<el-option
v-for="option in userTypeList"
:key="option.value"
:label="option.label"
:value="option.value">
</el-option>
</el-select>
<el-tree-select
style="width: 200px; margin-left: 16px"
placeholder="请选择所属组织"
v-model="dialogFilterData.organizationId"
:data="orgData"
:props="treeProps"
:render-after-expand="false"></el-tree-select>
<el-input
placeholder="请输入关键词"
v-model="dialogFilterData.search"
style="width: 200px; margin: 0 16px 0"
:prefix-icon="Search" />
<el-button type="primary" @click="dialogChangePage(1)">查询</el-button>
<el-button type="default" @click="resetFilter">重置</el-button>
</div>
</div>
<div class="table_content clearfix">
<bg-table
ref="dialogBgTable"
height="430"
select
rowKey="systemId"
:headers="detailHeaders"
:rows="dialogTableRows"
:stripe="true"
@selectAc="selectChange">
<template v-slot:isAdmin="{ row }">
<span>{{ userTypeConfig[row.isAdmin] }}</span>
</template>
</bg-table>
<bg-pagination
:page="dialogPageConfig.page"
:size="dialogPageConfig.limit"
:total="dialogPageConfig.total"
@change-page="dialogChangePage"
@change-size="dialogChangeSize">
</bg-pagination>
</div>
<div class="operate_btns" style="border-top: none">
<el-button @click="dialogMaintain = false"> 取消 </el-button>
<el-button type="primary" @click="save"> 保存 </el-button>
</div>
</div>
</el-dialog>
</div>
<div class="operate_btns">
<el-button type="primary" @click="submit"> 保存 </el-button>
</div>
</div>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { reactive, toRefs, computed, ref, onBeforeMount, nextTick } from "vue";
import { useRouter } from "vue-router";
import { useStore } from "vuex";
import axios from "../../../../../request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const router = useRouter();
const { getters } = useStore();
const userTypeConfig = computed(() => getters.getUserTypeConfig || {});
const userTypeList = computed(() =>
Object.keys(userTypeConfig.value).map((key) => ({
label: userTypeConfig.value[key],
value: +key,
}))
);
const dialogBgTable = ref(null);
const state = reactive({
//表单数据
formData: {
ruleName: "",
ruleType: "",
ruleDetail: "",
state: 0,
userScope: [],
},
//表单校验规则
rules: {
ruleName: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
ruleType: [
{
required: true,
message: "请选择",
trigger: "blur",
},
],
ruleDetail: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
userScope: [
{
required: true,
message: "请选择",
blur: "change",
},
],
},
dialogFilterData: {
isAdmin: "",
organizationId: "",
search: "",
},
pageConfig: {
page: 1,
limit: 10,
total: 0,
},
//弹框中表格分页配置项
dialogPageConfig: {
page: 1,
limit: 10,
total: 0,
},
//表格数据
tableRows: [],
//被选中的所有数据
selectedRows: {},
//弹框中表格数据
dialogTableRows: [],
//已选择数据条数
selectedNum: 0,
detailHeaders: [
{
label: "账号",
prop: "systemAccount",
// minWidth: 280,
},
{
label: "类型",
prop: "isAdmin",
// minWidth: 360,
},
{
label: "用户手机号",
prop: "phone",
// width: 200,
},
{
label: "所属组织",
prop: "name",
// width: 120,
},
],
orgData: [],
treeProps: {
label: "name",
children: "Child",
value: "organization_id",
},
//用户维护弹框
dialogMaintain: false,
});
const {
formData,
rules,
dialogFilterData,
pageConfig,
dialogPageConfig,
tableRows,
dialogTableRows,
selectedNum,
detailHeaders,
orgData,
treeProps,
dialogMaintain,
} = toRefs(state);
onBeforeMount(() => {
getOrgTree();
getTableList();
});
//用户维护
const userMaintain = () => {
state.dialogMaintain = true;
};
//获取所属组织级联数据
const getOrgTree = () => {
axios.get(`/apaas/system/v5/org/tree`).then((res) => {
if (res.data.code == 200) {
const orgDataTemp = res.data.data || [];
state.orgData = orgDataTemp;
} else {
ElMessage.error(res.data.data);
}
});
};
const changePage = (page) => {
state.pageConfig.page = page;
state.tableRows = state.selectedRows.slice((page - 1) * state.pageConfig.limit, page * state.pageConfig.limit);
};
const changeSize = (size) => {
state.pageConfig.limit = size;
changePage(1);
};
const dialogChangePage = (page) => {
state.dialogPageConfig.page = page;
getTableList();
};
const dialogChangeSize = (size) => {
state.dialogPageConfig.limit = size;
dialogChangePage(1);
};
//获取用户维护数据
const getTableList = () => {
const params = {
limit: state.dialogPageConfig.limit,
page: state.dialogPageConfig.page,
disp: 1,
...state.dialogFilterData,
};
axios.get("/apaas/system/v5/accessRule/listRuleUser", { params }).then((res) => {
if (res.data.code == 200) {
state.dialogTableRows = res.data.data || [];
state.dialogPageConfig.total = res.data.total || 0;
} else {
ElMessage.error(res.data.data);
}
});
};
//重置查询参数
const resetFilter = () => {
Object.keys(state.dialogFilterData).forEach((key) => {
state.dialogFilterData[key] = "";
});
dialogChangePage(1);
};
//用户维护弹框中清空
const cleanSelected = () => {
dialogBgTable.value.clearTable();
};
//用户维护选择项发生变化
const selectChange = (params) => {
state.selectedNum = params.allLength;
};
//保存
const save = () => {
state.dialogMaintain = false;
const res = dialogBgTable.value.dealSelectData();
state.selectedRows = Object.values(res);
state.formData.userScope = state.selectedRows.map((item) => ({
systemId: item.systemId,
}));
state.pageConfig.total = state.formData.userScope.length;
changePage(1);
};
//提交
const submit = () => {
axios.post("/apaas/system/v5/accessRule/addAccessRule", state.formData).then((res) => {
if (res.data.code == 200) {
ElMessage.success("添加成功");
router.push("/config/accessRule");
} else {
ElMessage.error(res.data.data);
}
});
};
</script>
<style lang="scss" scoped>
.page_container {
.main_container {
position: relative;
margin: 0 0 16px;
width: 100%;
height: calc(100% - 62px);
padding: 40px 0 70px;
overflow: auto;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
.form_content {
overflow: auto;
height: 100%;
padding-left: 40px;
.el-form {
:deep() .el-form-item {
display: block;
margin-bottom: 24px;
width: 60%;
.el-form-item__label {
display: block;
margin-bottom: 6px;
height: 14px;
line-height: 14px;
}
&.switch-item {
display: flex;
align-items: center;
.el-form-item__label {
display: inline-flex;
margin-bottom: 0px;
}
.el-switch {
height: 24px;
}
}
&.userScope {
width: 100%;
margin-bottom: 16px;
.el-button {
width: 92px;
}
.el-form-item__label {
margin-bottom: 16px;
}
.el-form-item__content {
display: block;
}
.table_content {
padding-right: 40px;
margin-top: 16px;
}
}
}
}
}
:deep() .dialog_box_maintain {
.el-dialog__body {
padding: 0 0 70px 0;
height: 630px;
}
.form_filter {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
height: 69px;
border-bottom: 1px solid #e6e9ef;
.left {
font-size: 0px;
.sleceted_tip {
margin: 0 24px 0;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
}
.right {
margin-right: 16px;
.el-button {
width: 64px;
&:last-child {
margin-left: 16px;
}
}
}
}
.table_content {
padding: 0 16px;
}
.operate_btns {
.el-button {
width: 64px;
&:first-child {
margin-right: 4px;
}
}
}
}
.operate_btns {
position: absolute;
bottom: 0;
width: 100%;
height: 70px;
text-align: right;
line-height: 68px;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
margin-right: 16px;
}
}
}
}
</style>
<!-- 角色管理 -->
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="form_content apaas_scroll_nor">
<el-form ref="refForm" label-position="left" :model="formData" :rules="rules" label-width="82px">
<div class="top_content">
<el-form-item label="规则名称" prop="ruleName">
<el-input placeholder="请输入" maxlength="100" v-model="formData.ruleName"></el-input>
</el-form-item>
<el-form-item label="规则类型" prop="ruleType">
<el-select placeholder="请选择" style="width: 100%" v-model="formData.ruleType">
<el-option label="IP" :value="1"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="规则" prop="ruleDetail">
<el-input
placeholder="请输入"
rows="5"
maxlength="300"
show-word-limit
type="textarea"
v-model="formData.ruleDetail" />
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>输入IP段,不可重复,例如:192.168.0.0/24</span><br />
<span>输入IP地址,例如192.168.0.1(支持ipv4地址,配置多个IP时使用回车符分隔)</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
<el-form-item class="switch-item" label="是否启用" prop="state" required>
<bg-switch :labels="['否', '是']" :values="[0, 1]" v-model="formData.state"></bg-switch>
</el-form-item>
</div>
<el-form-item class="userScope" label="用户范围" prop="userScope">
<el-button type="primary" @click="userMaintain"> 用户维护 </el-button>
<div class="table_content clearfix">
<bg-table ref="bgTable" :headers="detailHeaders" :rows="tableRows" height="550" :stripe="true">
<template v-slot:isAdmin="{ row }">
<span>{{ userTypeConfig[row.isAdmin] }}</span>
</template>
</bg-table>
<bg-pagination
:page="pageConfig.page"
:size="pageConfig.limit"
:total="pageConfig.total"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</el-form-item>
</el-form>
<el-dialog class="dialog_box_maintain" title="用户维护" v-model="dialogMaintain" width="1062px">
<div class="content_detail">
<div class="form_filter">
<div class="left">
<span class="sleceted_tip"
>已选择<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
<div class="right">
<el-select
placeholder="全部类型"
v-model="dialogFilterData.isAdmin"
style="width: 200px; margin-left: 16px">
<el-option
v-for="option in userTypeList"
:key="option.value"
:label="option.label"
:value="option.value">
</el-option>
</el-select>
<el-tree-select
style="width: 200px; margin-left: 16px"
placeholder="请选择所属组织"
v-model="dialogFilterData.organizationId"
:data="orgData"
:props="treeProps"
:render-after-expand="false"></el-tree-select>
<el-input
placeholder="请输入关键词"
v-model="dialogFilterData.search"
style="width: 200px; margin: 0 16px 0"
:prefix-icon="Search" />
<el-button type="primary" @click="dialogChangePage(1)">查询</el-button>
<el-button type="default" @click="resetFilter">重置</el-button>
</div>
</div>
<div class="table_content clearfix">
<bg-table
ref="dialogBgTable"
height="430"
select
rowKey="systemId"
:headers="detailHeaders"
:rows="dialogTableRows"
:originSelectedData="selectedRows"
:stripe="true"
@selectAc="selectChange">
<template v-slot:isAdmin="{ row }">
<span>{{ userTypeConfig[row.isAdmin] }}</span>
</template>
</bg-table>
<bg-pagination
:page="dialogPageConfig.page"
:size="dialogPageConfig.limit"
:total="dialogPageConfig.total"
@change-page="dialogChangePage"
@change-size="dialogChangeSize">
</bg-pagination>
</div>
<div class="operate_btns" style="border-top: none">
<el-button @click="dialogMaintain = false"> 取消 </el-button>
<el-button type="primary" @click="save"> 保存 </el-button>
</div>
</div>
</el-dialog>
</div>
<div class="operate_btns">
<el-button type="primary" @click="submit"> 保存 </el-button>
</div>
</div>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { reactive, toRefs, computed, ref, onBeforeMount, nextTick } from "vue";
import { useRoute, useRouter } from "vue-router";
import { useStore } from "vuex";
import axios from "../../../../../request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const route = useRoute();
const router = useRouter();
const { getters } = useStore();
const userTypeConfig = computed(() => getters.getUserTypeConfig || {});
const userTypeList = computed(() =>
Object.keys(userTypeConfig.value).map((key) => ({
label: userTypeConfig.value[key],
value: +key,
}))
);
const dialogBgTable = ref(null);
const state = reactive({
//表单数据
formData: {
ruleName: "",
ruleType: "",
ruleDetail: "",
state: 0,
userScope: [],
},
//表单校验规则
rules: {
ruleName: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
ruleType: [
{
required: true,
message: "请选择",
trigger: "blur",
},
],
ruleDetail: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
userScope: [
{
required: true,
message: "请选择",
blur: "change",
},
],
},
dialogFilterData: {
isAdmin: "",
organizationId: "",
search: "",
},
pageConfig: {
page: 1,
limit: 10,
total: 0,
},
//弹框中表格分页配置项
dialogPageConfig: {
page: 1,
limit: 10,
total: 0,
},
//表格数据
tableRows: [],
//被选中的所有数据
selectedRows: {},
//弹框中表格数据
dialogTableRows: [],
//已选择数据条数
selectedNum: 0,
detailHeaders: [
{
label: "账号",
prop: "systemAccount",
// minWidth: 280,
},
{
label: "类型",
prop: "isAdmin",
// minWidth: 360,
},
{
label: "用户手机号",
prop: "phone",
// width: 200,
},
{
label: "所属组织",
prop: "name",
// width: 120,
},
],
orgData: [],
treeProps: {
label: "name",
children: "Child",
value: "organization_id",
},
//用户维护弹框
dialogMaintain: false,
});
const {
formData,
rules,
dialogFilterData,
pageConfig,
dialogPageConfig,
tableRows,
selectedRows,
dialogTableRows,
selectedNum,
detailHeaders,
orgData,
treeProps,
dialogMaintain,
} = toRefs(state);
onBeforeMount(() => {
echoFormData();
getOrgTree();
getTableList();
});
//回显编辑数据
const echoFormData = () => {
const query = route.query;
getUserScopeList();
state.formData.ruleName = query.ruleName;
state.formData.state = +query.state;
state.formData.ruleType = +query.ruleType;
state.formData.ruleDetail = query.ruleDetail;
};
//用户维护
const userMaintain = () => {
state.dialogMaintain = true;
};
//获取所属组织级联数据
const getOrgTree = () => {
axios.get(`/apaas/system/v5/org/tree`).then((res) => {
if (res.data.code == 200) {
const orgDataTemp = res.data.data || [];
state.orgData = orgDataTemp;
} else {
ElMessage.error(res.data.data);
}
});
};
const changePage = (page) => {
state.pageConfig.page = page;
state.tableRows = state.selectedRows.slice((page - 1) * state.pageConfig.limit, page * state.pageConfig.limit);
};
const changeSize = (size) => {
state.pageConfig.limit = size;
changePage(1);
};
const dialogChangePage = (page) => {
state.dialogPageConfig.page = page;
getTableList();
};
const dialogChangeSize = (size) => {
state.dialogPageConfig.limit = size;
dialogChangePage(1);
};
//编辑时回显用户范围数据
const getUserScopeList = () => {
const params = {
limit: 10000,
page: 1,
id: route.query.id,
disp: 0,
};
axios.get("/apaas/system/v5/accessRule/listRuleUser", { params }).then((res) => {
if (res.data.code == 200) {
state.selectedRows = res.data.data || [];
state.pageConfig.total = state.selectedRows.length;
state.selectedNum = state.selectedRows.length;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
};
//获取用户维护数据
const getTableList = () => {
const params = {
limit: state.dialogPageConfig.limit,
page: state.dialogPageConfig.page,
disp: 1,
...state.dialogFilterData,
};
axios.get("/apaas/system/v5/accessRule/listRuleUser", { params }).then((res) => {
if (res.data.code == 200) {
state.dialogTableRows = res.data.data || [];
state.dialogPageConfig.total = res.data.total || 0;
} else {
ElMessage.error(res.data.data);
}
});
};
//重置查询参数
const resetFilter = () => {
Object.keys(state.dialogFilterData).forEach((key) => {
state.dialogFilterData[key] = "";
});
dialogChangePage(1);
};
//用户维护弹框中清空
const cleanSelected = () => {
dialogBgTable.value.clearTable();
};
//用户维护选择项发生变化
const selectChange = (params) => {
state.selectedNum = params.allLength;
};
//保存
const save = () => {
state.dialogMaintain = false;
const res = dialogBgTable.value.dealSelectData();
state.selectedRows = Object.values(res);
state.formData.userScope = state.selectedRows.map((item) => ({
systemId: item.systemId,
}));
state.pageConfig.total = state.formData.userScope.length;
changePage(1);
};
//提交
const submit = () => {
const params = {
ruleId: route.query.ruleId,
...state.formData,
};
axios.put("/apaas/system/v5/accessRule/updateAccessRule", params).then((res) => {
if (res.data.code == 200) {
ElMessage.success("更新成功");
router.push("/config/accessRule");
} else {
ElMessage.error(res.data.data);
}
});
};
</script>
<style lang="scss" scoped>
.page_container {
.main_container {
position: relative;
margin: 0 0 16px;
width: 100%;
height: calc(100% - 62px);
padding: 40px 0 70px;
overflow: auto;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
.form_content {
overflow: auto;
height: 100%;
padding-left: 40px;
.el-form {
:deep() .el-form-item {
display: block;
margin-bottom: 24px;
width: 60%;
.el-form-item__label {
display: block;
margin-bottom: 6px;
height: 14px;
line-height: 14px;
}
&.switch-item {
display: flex;
align-items: center;
.el-form-item__label {
display: inline-flex;
margin-bottom: 0px;
}
.el-switch {
height: 24px;
}
}
&.userScope {
width: 100%;
margin-bottom: 16px;
.el-button {
width: 92px;
}
.el-form-item__label {
margin-bottom: 16px;
}
.el-form-item__content {
display: block;
}
.table_content {
padding-right: 40px;
margin-top: 16px;
}
}
}
}
}
:deep() .dialog_box_maintain {
.el-dialog__body {
padding: 0 0 70px 0;
height: 630px;
}
.form_filter {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
height: 69px;
border-bottom: 1px solid #e6e9ef;
.left {
font-size: 0px;
.sleceted_tip {
margin: 0 24px 0;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
}
.right {
margin-right: 16px;
.el-button {
width: 64px;
&:last-child {
margin-left: 16px;
}
}
}
}
.table_content {
padding: 0 16px;
}
.operate_btns {
.el-button {
width: 64px;
&:first-child {
margin-right: 4px;
}
}
}
}
.operate_btns {
position: absolute;
bottom: 0;
width: 100%;
height: 70px;
text-align: right;
line-height: 68px;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
margin-right: 16px;
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入规则名称或规则">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="addRule">
<bg-icon
style="font-size: 12px; color: #fff; margin-right: 8px"
icon="#bg-ic-add"></bg-icon>
新增
</el-button>
<el-button @click="openDeleteDialog(2)"> 批量删除 </el-button>
<span class="sleceted_tip"
>已选<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">修改时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px" />
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<bg-table
ref="rulesTable"
:headers="headers"
:rows="tableRows"
:stripe="true"
select
@selectAc="selectRows"
canEdit
canEditFlag="state">
<template v-slot:rule_type="{ row }">
<span>{{ ["", "IP"][row.rule_type] }}</span>
</template>
<template v-slot:num_user="{ row }">
<span class="can_click_text under_line" @click="openUserDialog(row)">{{
row.num_user
}}</span>
</template>
<template v-slot:state="{ row }">
<bg-switch
@click="stateChange(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.state"></bg-switch>
</template>
<template v-slot:updated_time="{ row }">
<span>{{ dateStringTransform(row.updated_time) }}</span>
</template>
<template v-slot:action="{ row }">
<bg-table-btns2 :limit="3" :key="row.id">
<bg-table-btn @click="edit(row)">编辑</bg-table-btn>
<bg-table-btn :disabled="row.state == 1" @click="openDeleteDialog(1, row)"
>删除</bg-table-btn
>
</bg-table-btns2>
</template>
</bg-table>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
<el-dialog class="dialog_box_detail" title="用户详情" v-model="dialogDetail" width="1062px">
<div class="content_detail">
<div class="form_filter">
<div class="left">
<el-select
v-model="userFilter.isAdmin"
placeholder="请选择"
style="width: 200px; margin-left: 16px">
<el-option
v-for="(item, index) in userTypeList"
:key="'pushOptions' + index"
:label="item.name"
:value="item.value">
</el-option>
</el-select>
<el-cascader
v-model="userFilter.organizationId"
:options="orgList"
placeholder="请选择组织"
:props="{
expandTrigger: 'hover',
label: 'name',
value: 'organization_id',
emitPath: false,
checkStrictly: true,
children: 'Child',
}"
:clearable="true"
collapse-tags
style="width: 200px; margin-left: 16px">
<template #default="{ data }">
<span>{{ data.name }}</span>
</template>
</el-cascader>
<el-input
placeholder="请输入关键词"
v-model.trim="userFilter.search"
style="width: 200px; margin-left: 16px"
:prefix-icon="Search" />
</div>
<div class="right">
<el-button type="primary" @click="searchUsers">查询</el-button>
<el-button type="default" @click="clearAction">重置</el-button>
</div>
</div>
<div class="table_content">
<bg-table
ref="bgUserTable"
:headers="userHeaders"
:rows="userTableRows"
height="430"
:stripe="true">
<template v-slot:isAdmin="{ row }">
<span>{{ ["", "业务系统用户", "组织管理员", "平台用户"][row.isAdmin] }}</span>
</template>
</bg-table>
<bg-pagination
:page="userFilter.page"
:size="userFilter.limit"
:total="userTableTotal"
@change-page="changeUserPage"
@change-size="changeUserSize">
</bg-pagination>
</div>
</div>
</el-dialog>
<el-dialog
class="dialog_box"
:title="deleteDialogConfig.title"
v-model="dialogDelete"
width="400px">
<div style="text-align: center">
{{ deleteDialogConfig.tips }}
</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="confimDelete">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, computed, onBeforeMount, ref } from "vue";
import { Search } from "@element-plus/icons-vue";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import store from "@/store";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { dateStringTransform } from "@/services/helper.js";
const router = useRouter();
const rulesTable = ref(null);
const state = reactive({
filter: {
search: "",
page: 1,
limit: 10,
time: [],
},
headers: [
{
label: "规则名称",
prop: "rule_name",
},
{
label: "规则类型",
prop: "rule_type",
},
{
label: "用户数量",
prop: "num_user",
},
{
label: "规则",
prop: "rule_detail",
},
{
label: "是否启用",
prop: "state",
},
{
label: "修改时间",
prop: "updated_time",
width: 240,
},
{
label: "操作",
prop: "action",
width: 120,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
selectedNum: 0,
actionRow: {},
dialogDetail: false,
userFilter: {
page: 1,
limit: 10,
search: "",
isAdmin: "",
organizationId: "",
disp: 0,
},
userTypeList: [
{
name: "全部类型",
value: "",
},
{
name: "业务系统用户",
value: 1,
},
{
name: "组织管理员",
value: 2,
},
{
name: "平台用户",
value: 3,
},
],
orgList: [],
userHeaders: [
{
label: "账号",
prop: "systemAccount",
},
{
label: "类型",
prop: "isAdmin",
},
{
label: "用户手机号",
prop: "phone",
},
{
label: "所属组织",
prop: "name",
},
],
userTableRows: [],
userTableTotal: 0,
selection: [],
dialogDelete: false,
deleteDialogConfig: {
title: "删除",
ruleId: [],
tips: "确定要删除当前行数据吗?",
},
});
const getOrgList = () => {
axios.get(`/apaas/system/v5/org/tree`).then((res) => {
if (res.data.code == 200) {
state.orgList = res.data.data || [];
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取组织
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 关键字
const changePage = (page) => {
state.filter.page = page;
getTableRows();
}; // 改变页码
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
}; // 改变每页条数
const filterAction = () => {
changePage(1);
}; // 高级搜索中查询
const filterClear = () => {
state.filter = {
search: "",
page: 1,
limit: 10,
time: [],
};
changePage(1);
}; // 高级搜索中重置
const getTableRows = () => {
let params = { ...state.filter };
params.updatedTimeFrom = params.time ? params.time[0] || "" : "";
params.updatedTimeTo = params.time ? params.time[1] || "" : "";
delete params.time;
axios
.get(`/apaas/system/v5/accessRule/listAccessRule`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取表格数据
const selectRows = (data) => {
state.selectedNum = data.allLength;
state.selection = data.selection;
};
// 新增规则跳转
const addRule = () => {
router.push("/config/accessRule/add");
};
const cleanSelected = () => {
rulesTable.value.clearTable();
}; //清空
// 改变规则状态
const stateChange = (row) => {
axios
.put(`/apaas/system/v5/accessRule/updateState?id=${row.id}&state=${row.state}`)
.then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
changePage(1);
} else {
ElMessage.error(res.data.data);
row.state = row.state == 0 ? 1 : 0;
}
});
};
// 打开用户弹窗
const openUserDialog = (row) => {
state.dialogDetail = true;
state.actionRow = row;
clearAction();
};
const searchUsers = () => {
changeUserPage(1);
}; // 用户弹窗查询
// 用户弹窗重置
const clearAction = () => {
state.userFilter = {
page: 1,
limit: 10,
search: "",
isAdmin: "",
organizationId: "",
disp: 0,
};
changeUserPage(1);
};
// 用户详情改变分页
const changeUserPage = (page) => {
state.userFilter.page = page;
getUserTableRows();
};
const changeUserSize = (size) => {
state.userFilter.limit = size;
changeUserPage(1);
};
// 用户详情弹窗数据
const getUserTableRows = () => {
let params = {
...state.userFilter,
id: state.actionRow.id,
};
axios.get(`/apaas/system/v5/accessRule/listRuleUser`, { params }).then((res) => {
if (res.data.code == 200) {
state.userTableRows = res.data.data || [];
state.userTableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
const edit = (row) => {
router.push({
path: "/config/accessRule/edit",
query: {
id: row.id,
ruleId: row.rule_id,
ruleName: row.rule_name,
state: row.state,
ruleType: row.rule_type,
ruleDetail: row.rule_detail,
},
});
}; //编辑
//打开删除弹窗
const openDeleteDialog = (type, row) => {
if (type === 2) {
const res = rulesTable.value.dealSelectData();
const list = Object.values(res);
if (list.length === 0) return ElMessage.warning("请选择要删除的数据");
state.dialogDelete = true;
const ruleId = list.map((item) => item.rule_id);
state.deleteDialogConfig = { title: "批量删除", ruleId, tips: "确定要删除所选数据吗?" };
return;
}
state.dialogDelete = true;
state.deleteDialogConfig = {
title: "删除",
ruleId: [row.rule_id],
tips: "确定要删除当前行数据吗?",
};
};
//确定删除
const confimDelete = () => {
axios
.delete("/apaas/system/v5/accessRule/delAccessRule", {
data: {
ruleId: state.deleteDialogConfig.ruleId,
},
})
.then((res) => {
if (res.data.code == 200) {
ElMessage.success("删除成功");
changePage(1);
} else {
ElMessage.error(res.data.data);
}
})
.finally(() => {
state.dialogDelete = false;
});
};
const {
filter,
headers,
userHeaders,
tableRows,
tableTotal,
selectedNum,
dialogDetail,
userTableRows,
userTableTotal,
userFilter,
userTypeList,
orgList,
selection,
dialogDelete,
deleteDialogConfig,
} = toRefs(state);
onBeforeMount(() => {
getTableRows();
getOrgList();
});
</script>
<style lang="scss" scoped>
.page_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
padding: 0 24px;
min-height: 100%;
.main_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex-grow: 1;
width: 100%;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
padding: 0 0 16px 0;
margin-bottom: 16px;
position: relative;
.sleceted_tip {
margin: 0 24px 0 40px;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
.filter_item {
display: flex;
align-items: center;
.filter_title {
width: 84px;
white-space: pre-wrap;
font-weight: normal;
}
}
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
padding: 0 16px;
.el-table {
flex: 1;
.under_line {
text-decoration: underline;
}
}
}
}
:deep() .dialog_box_detail {
.el-dialog__body {
padding: 0 0 18px 0;
height: 580px;
}
.form_filter {
display: flex;
justify-content: space-between;
align-items: center;
height: 69px;
border-bottom: 1px solid #e6e9ef;
.right {
margin-right: 16px;
.el-button {
width: 64px;
&:last-child {
margin-left: 16px;
}
}
}
}
.table_content {
padding: 0 16px;
}
}
}
</style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group @search="changeSearch" v-model="filter.search" placeholder="请输入容器平台名称">
<template v-slot:left_action>
<div class="apaas_button">
<el-button class="register_btn" type="primary" @click="register">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<div class="table bg-scroll">
<bg-table
ref="Bgtable"
:headers="headers"
:rows="tableRows"
isIndex
:paddingLeft="32"
:indexWidth="108"
:stripe="true">
<template v-slot:platform_name="{ row }">
<span class="can_click_text" @click="viewDetail(row)">
{{ row.platform_name }}
</span>
</template>
<template v-slot:platform_type="{ row }">
{{ ["", "自有平台", "第三方平台"][row.platform_type] }}
</template>
<template v-slot:action="{ row }">
<bg-table-btn
class="can_click_text"
:click="
() => {
edit_row(row);
}
">
编辑
</bg-table-btn>
<bg-table-btn
class="can_click_text"
:click="
() => {
delete_row(row);
}
"
:disabled="row.state == 1 || row.role_type == 1">
删除
</bg-table-btn>
</template>
</bg-table>
</div>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
<!-- 新增/编辑弹窗 -->
<el-dialog
class="dialog-box"
:title="dialogType == 1 ? '新增' : '编辑'"
:close-on-click-modal="false"
v-model="addDialog"
width="614px">
<el-form
class="bg_form"
ref="bgForm"
:model="formData"
:rules="rules"
label-width="130px"
style="margin-top: 24px">
<el-form-item label="平台类型" prop="platform_type">
<el-radio-group v-model="formData.platform_type">
<el-radio v-for="(item, index) in typeList" :key="'platform_type' + index" :label="item.value">
{{ item.name }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="平台名称" prop="platform_name">
<el-input v-model.trim="formData.platform_name" placeholder="请输入平台名称" maxlength="50" show-word-limit />
</el-form-item>
<el-form-item label="平台帐号" prop="platform_account" v-if="formData.platform_type == 1">
<el-input
v-model.trim="formData.platform_account"
placeholder="请输入aPaaS在该平台注册的帐号"
maxlength="50"
show-word-limit />
</el-form-item>
<el-form-item label="工作组名称" prop="work_group" v-if="formData.platform_type == 1">
<el-input
v-model.trim="formData.work_group"
placeholder="请输入aPaaS在该平台注册申请的工作组名称"
maxlength="50"
show-word-limit />
</el-form-item>
<el-form-item label="服务地址" prop="service_url" v-if="formData.platform_type == 1">
<el-input
v-model.trim="formData.service_url"
placeholder="请输入服务地址URL"
maxlength="100"
show-word-limit />
</el-form-item>
<el-form-item label="服务地址TOKEN" prop="service_token" v-if="formData.platform_type == 1">
<el-input
v-model.trim="formData.service_token"
placeholder="请输入服务地址的TOKEN参数"
maxlength="100"
show-word-limit />
</el-form-item>
<el-form-item label="APISERVER" prop="apiserver">
<el-input
v-model.trim="formData.apiserver"
placeholder="请输入APISERVER参数"
maxlength="100"
show-word-limit />
</el-form-item>
<el-form-item label="SERVER TOKEN" prop="token">
<el-input
v-model.trim="formData.token"
placeholder="请输入SERVER TOKEN参数"
maxlength="100"
show-word-limit />
</el-form-item>
<el-form-item label="描述">
<el-input
v-model.trim="formData.platform_desc"
placeholder="请输入描述"
maxlength="300"
show-word-limit
:rows="4"
type="textarea" />
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="apaas_button">
<el-button
type="default"
@click="
() => {
addDialog = false;
dialogType = 0;
}
"
>取消</el-button
>
<el-button type="info" @click="testUrl">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-checklist"></bg-icon>测试连接</el-button>
<el-button type="primary" :disabled="!testSuccess" @click="postData">保存</el-button>
</div>
</template>
</el-dialog>
<!-- 平台详情 -->
<el-dialog class="dialog_box" title="平台详情" v-model="detailDialog" width="600px">
<div>
<bg-info class="info_box" :data="platfromInfo" :col="1"></bg-info>
</div>
</el-dialog>
<!-- 删除弹窗 -->
<el-dialog class="dialog_box" title="提示" v-model="delDialog" width="400px">
<div>删除后,aPaaS平台将不再与该容器平台对接,确认删除吗?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="delDialog = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick, watch } from "vue";
import axios from "../../../../request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const Bgtable = ref(null);
const bgForm = ref(null);
const state = reactive({
filter: {
page: 1,
limit: 10,
search: "",
},
headers: [
{
label: "容器平台名称",
prop: "platform_name",
},
{
label: "平台类型",
prop: "platform_type",
},
{
label: "描述",
prop: "platform_desc",
minWidth: 346,
},
{
label: "创建人",
prop: "created_by",
},
{
label: "创建时间",
prop: "created_time",
width: 276,
},
{
label: "操作",
prop: "action",
width: 130,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
actionRow: {}, // 当前选中的数据
delDialog: false, // 删除弹窗
dialogType: 1, // 1-新增 2-编辑
addDialog: false, // 新增/编辑弹窗
formData: {
platform_type: 1,
platform_name: "",
platform_account: "",
work_group: "",
service_url: "",
service_token: "",
apiserver: "",
token: "",
platform_desc: "",
},
rules: {
platform_name: [{ required: true, message: "平台名称不能为空", trigger: "blur" }],
platform_type: [{ required: true, message: "请选择平台类型", trigger: "change" }],
platform_account: [{ required: true, message: "平台帐号不能为空", trigger: "blur" }],
work_group: [{ required: true, message: "工作组名称不能为空", trigger: "blur" }],
service_url: [{ required: true, message: "服务地址URL不能为空", trigger: "blur" }],
service_token: [{ required: true, message: "服务地址的TOKEN参数不能为空", trigger: "blur" }],
apiserver: [{ required: true, message: "APISERVER参数不能为空", trigger: "blur" }],
token: [{ required: true, message: "SERVER TOKEN参数不能为空", trigger: "blur" }],
},
typeList: [
{
name: "自有平台",
value: 1,
},
{
name: "第三方平台",
value: 2,
},
],
detailDialog: false, // 详情弹窗
platfromInfo: [
{
name: "平台类型",
value: "",
nameWidth: 144,
},
{
name: "平台名称",
value: "",
nameWidth: 144,
},
{
name: "平台帐号",
value: "",
nameWidth: 144,
},
{
name: "工作组名称",
value: "",
nameWidth: 144,
},
{
name: "服务地址",
value: "",
nameWidth: 144,
},
{
name: "TOKEN",
value: "",
nameWidth: 144,
},
{
name: "APISERVER",
value: "",
nameWidth: 144,
},
{
name: "TOKEN",
value: "",
nameWidth: 144,
},
{
name: "描述",
value: "",
nameWidth: 144,
},
],
testSuccess: false, // 测试连接结果
});
watch(
() => state.formData,
(newValue, oldValue) => {
state.testSuccess = false;
},
{
deep: true,
}
);
const getRoleRows = () => {
let params = { ...state.filter };
axios.get(`/apaas/application/v5/platform/list`, { params }).then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取列表
const changePage = (val) => {
state.filter.page = val;
getRoleRows();
}; // 改变页码
const changeSize = (val) => {
state.filter.limit = val;
changePage(1);
}; // 改变每页条数
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 表格关键字筛选
const register = () => {
resetForm();
state.dialogType = 1;
state.addDialog = true;
}; // 新增
const resetForm = () => {
state.formData = {
platform_type: 1,
platform_name: "",
platform_account: "",
work_group: "",
service_url: "",
service_token: "",
apiserver: "",
token: "",
platform_desc: "",
};
if (bgForm.value) {
nextTick(() => {
bgForm.value.validate((valid) => {
if (!valid) {
bgForm.value.clearValidate();
}
});
});
}
}; // 重置表单
const viewDetail = async (row) => {
let { data } = await getDetail(row.id);
if (data.code == 200) {
state.platfromInfo[0].value = ["", "自有平台", "第三方平台"][data.data.platform_type];
state.platfromInfo[1].value = data.data.platform_name;
state.platfromInfo[2].value = data.data.platform_account;
state.platfromInfo[3].value = data.data.work_group;
state.platfromInfo[4].value = data.data.service_url;
state.platfromInfo[5].value = data.data.service_token;
state.platfromInfo[6].value = data.data.apiserver;
state.platfromInfo[7].value = data.data.token;
state.platfromInfo[8].value = data.data.platform_desc;
if (data.data.platform_type == 2) {
state.platfromInfo.splice(2, 4);
}
state.detailDialog = true;
} else {
ElMessage.error(data.data);
}
}; // 查看详情
const getDetail = (id) => {
return axios.get(`/apaas/application/v5/platform/detail/${id}`);
}; // 获取详情
const edit_row = async (row) => {
resetForm();
let { data } = await getDetail(row.id);
if (data.code == 200) {
Object.assign(state.formData, data.data);
state.dialogType = 2;
state.addDialog = true;
} else {
ElMessage.error(data.data);
}
}; // 编辑
const testUrl = () => {
bgForm.value.validate((valid) => {
if (valid) {
let params = {
...state.formData,
};
axios.post(`/apaas/application/v5/platform/check`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success("测试连接通过");
state.testSuccess = true;
} else {
ElMessage.error("测试连接失败");
}
});
}
});
}; // 测试连接
const postData = () => {
let params = {
...state.formData,
};
let url = ["", "/apaas/application/v5/platform/add", "/apaas/application/v5/platform/update"];
axios.post(url[state.dialogType], params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.addDialog = false;
state.testSuccess = false;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
}; // 新建/编辑数据
const delete_row = (row) => {
state.actionRow = row;
state.delDialog = true;
}; // 删除
const deleteData = () => {
axios.delete(`/apaas/application/v5/platform/${state.actionRow.id}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.delDialog = false;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
}; // 确认删除
onBeforeMount(() => {
getRoleRows();
});
const {
filter,
headers,
tableRows,
tableTotal,
delDialog,
dialogType,
addDialog,
formData,
rules,
typeList,
detailDialog,
platfromInfo,
testSuccess,
} = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
.table_container {
height: calc(100% - 70px);
padding: 0 16px;
.table {
max-height: calc(100% - 64px);
}
}
}
}
</style>
<style>
.dialog-box .el-dialog__body {
padding: 0 24px;
}
.bg-info {
text-align: left;
}
</style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="flex_row">
<div class="flex_left">
<div class="box">
<el-input
class="type-input"
v-model.trim="typeKeyword"
@input="searchType"
:prefix-icon="Search"
placeholder="请输入搜索内容"></el-input>
<div class="type_station bg-scroll">
<div
class="type-box"
:class="{ 'current-type': nodeClassifyId == item.classify_id }"
@click="nodeClick(item)"
v-for="(item, index) in typeList"
:key="'type' + index + 200">
{{ item.classify_name }}
</div>
</div>
</div>
</div>
<div class="flex_right">
<div class="main_container">
<bg-filter-group @search="changeSearch" v-model="filter.search" placeholder="请输入关键字">
<template v-slot:left_action v-if="nodeClassifyId != '263758a4-0349-4d49-a816-e8ff8d33a8bb'">
<div class="apaas_button">
<el-button type="primary" @click="backDict" v-if="dictLevel != 1">
<!-- <bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon> -->
返回上级
</el-button>
<el-button type="primary" @click="register">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">状态</span>
<el-select v-model="filter.state" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in stateOptions"
:key="'pushOptions' + index"
:label="item.name"
:value="item.value">
</el-option>
</el-select>
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<div class="table bg-scroll">
<bg-table ref="bgTable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true">
<template v-slot:name="{ row }">
<span class="can_click_text" @click="getChildren(row)" v-if="row.children">
{{ row.name }}
</span>
<span v-else>
{{ row.name }}
</span>
</template>
<template v-slot:updated_time="{ row }">
{{ row.updated_time.split("+")[0].replace("T", " ").replace("Z", " ") }}
</template>
<template v-slot:state="{ row }">
<bg-switch
@click="changeUseRow(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.state"></bg-switch>
</template>
<template v-slot:action="{ row }">
<!-- 除技术类型之外的字典可以删除 -->
<bg-table-btns2 :limit="3" v-if="nodeClassifyId !== '263758a4-0349-4d49-a816-e8ff8d33a8bb'">
<bg-table-btn @click="edit_row(row)" :disabled="row.state == 1">编辑</bg-table-btn>
<bg-table-btn @click="delete_row(row)" :disabled="row.state == 1">删除</bg-table-btn>
<bg-table-btn @click="moveRow(row, 1)" :disabled="!row.canMoveUp">上移</bg-table-btn>
<bg-table-btn @click="moveRow(row, 2)" :disabled="!row.canMoveDown">下移</bg-table-btn>
</bg-table-btns2>
<!-- 技术类型字典不可删除 -->
<bg-table-btns2 :limit="3" v-else>
<bg-table-btn @click="edit_row(row)" :disabled="row.state == 1">编辑</bg-table-btn>
<bg-table-btn @click="moveRow(row, 1)" :disabled="!row.canMoveUp">上移</bg-table-btn>
<bg-table-btn @click="moveRow(row, 2)" :disabled="!row.canMoveDown">下移</bg-table-btn>
</bg-table-btns2>
</template>
</bg-table>
</div>
<bg-pagination
:page="filter.page"
:size="filter.size"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
</div>
</div>
<!-- 新增/编辑弹窗 -->
<el-dialog class="dialog_box" :title="addType == 1 ? '新增' : '编辑'" v-model="addDialog" width="758px">
<el-form ref="bgForm" :model="formData" :rules="rules" label-width="80px" class="bg_form">
<el-form-item label="名称" prop="name">
<el-input
v-model.trim="formData.name"
show-word-limit
:maxlength="
nodeClassifyId == 'eb9c7d70-c123-42b7-8e61-dde1b022b669'
? 6
: nodeClassifyId == 'efd9ec3b-7f18-49e2-9d88-bcca022243bb'
? 4
: nodeClassifyId == '949a1138-4995-464e-97a9-424d097eb271'
? 2
: 20
"
placeholder="请输入名称"></el-input>
</el-form-item>
<el-form-item label="描述" prop="describe">
<el-input
v-model="formData.describe"
type="textarea"
:autosize="{ minRows: 5 }"
show-word-limit
maxlength="200"
resize="vertical"
placeholder="请输入描述"></el-input>
</el-form-item>
<el-form-item label="是否启用" prop="state" style="margin-bottom: 0px">
<el-switch
class="bg-switch-ele"
v-model="formData.state"
:active-value="1"
:inactive-value="0"
inline-prompt
active-text="是"
inactive-text="否" />
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="addDialog = false">取 消</el-button>
<el-button type="primary" @click="addConfirm">保 存</el-button>
</div>
</template>
</el-dialog>
<!-- 删除弹窗 -->
<el-dialog
class="dialog_box"
title="提示"
v-model="dialogDelete"
width="400px"
:before-close="
() => {
dialogDelete = false;
}
">
<div>确定要删除此字典值吗?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, onBeforeMount, toRefs, computed, nextTick } from "vue";
import { ElMessage } from "element-plus";
import { Search } from "@element-plus/icons-vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "../../../../request/http.js";
const bgForm = ref(null);
const headers = computed(() => {
let _headers = [
{
label: "名称",
prop: "name",
},
{
label: "描述",
prop: "describe",
minWidth: 360,
},
{
label: "更新时间",
prop: "updated_time",
width: 220,
},
{
label: "是否启用",
prop: "state",
},
{
label: "操作",
prop: "action",
width: 176,
fixed: "right",
},
];
if (state.nodeClassifyId === "59315100-5c2a-4381-83fe-32934605f0a9") {
_headers.splice(1, 0, {
label: "版本数",
prop: "total_children",
});
}
if (state.nodeClassifyId === "b3a083ee-a8c1-8ca4-f9a1-ea01692a0f1a") {
_headers.splice(1, 0, {
label: "服务名称",
prop: "service_name",
});
}
return _headers;
});
const state = reactive({
bgForm,
typeList: [], // 分类数据
typeKeyword: "", // 分类删选关键词
nodeClassifyId: null, // 当前选中分类的uuid 用于新增字典
nodeId: null, // 当前选中分类的id 用于请求列表
timer: null, // 定时器
tableRows: [], // 表格数据
tableTotal: 0, // 表格数据条数
filter: {
state: "",
search: "",
page: 1,
limit: 10,
}, // 表格筛选项
stateOptions: [
{
name: "全部",
value: "",
},
{
name: "启用",
value: "1",
},
{
name: "禁用",
value: "0",
},
], // 启用禁用
actionRow: null, // 当前操作的数据
dialogDelete: false, // 删除弹窗
addType: 0, //
addDialog: false,
formData: {
name: "",
describe: "",
state: 1,
p_dict_id: "",
},
rules: {
name: [{ required: true, message: "请输入名称", trigger: "blur" }],
describe: [
{ required: true, message: "请输入描述", trigger: "blur" },
{ max: 200, message: "描述最大为200字", trigger: "blur" },
],
state: [{ required: true, message: "请选择是否启用", trigger: "change" }],
},
fatherRow: null,
});
const dictLevel = ref(1);
const getChildren = (row) => {
dictLevel.value = 2;
state.tableRows = row.children || [];
state.tableTotal = row.total_children;
state.fatherRow = row;
};
const backDict = () => {
dictLevel.value = 1;
state.fatherRow = null;
changePage(1);
};
const nodeClick = (item) => {
state.nodeId = item.id;
dictLevel.value = 1;
state.fatherRow = null;
state.nodeClassifyId = item.classify_id;
state.filter = {
state: "",
search: "",
page: 1,
limit: 10,
};
changePage(1);
}; // 切换字典分类
const searchType = () => {
if (state.timer) {
clearTimeout(state.timer);
}
state.timer = setTimeout(() => {
getTypeList();
}, 500);
}; // 字典分类筛选
const getTypeList = () => {
let params = {
name: state.typeKeyword,
};
axios
.get(`/apaas/system/v5/dictionary/classify/list`, { params })
.then((res) => {
if (res.data.code == 200) {
state.typeList = res.data.data || [];
state.nodeClassifyId = state.typeList[0].classify_id || null;
state.nodeId = state.typeList[0].id || null;
if (state.nodeId) {
getTableRows();
}
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
}; // 获取字典分类
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 表格关键字筛选
const filterAction = () => {
changePage(1);
}; // 查询按钮
const filterClear = () => {
state.filter = {
state: "",
search: "",
limit: 10,
page: 1,
};
changePage(1);
}; // 重置筛选项
const getTableRows = () => {
let params = { ...state.filter };
params.id = state.nodeId;
axios
.get(`/apaas/system/v5/dictionary/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
let data = res.data.data || [];
if (dictLevel.value == 1) {
state.tableRows = data;
state.tableTotal = res.data.total;
} else {
let row = data.filter((e) => {
return e.dict_id == state.fatherRow.dict_id;
});
state.tableRows = row[0].children;
state.tableTotal = row[0].total_children;
}
if (state.tableRows.length > 0) {
state.tableRows.forEach((e) => {
e.canMoveUp = true;
e.canMoveDown = true;
});
state.tableRows[0].canMoveUp = false;
state.tableRows[state.tableRows.length - 1].canMoveDown = false;
}
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取表格数据
const changeUseRow = (row) => {
axios.put(`/apaas/system/v5/dictionary/state?id=${row.id}&state=${row.state}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
changePage(1);
} else {
ElMessage.error(res.data.data);
row.state = row.state == 0 ? 1 : 0;
}
});
}; // 启用禁用
const changePage = (page) => {
state.filter.page = page;
getTableRows();
}; // 改变页码
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
}; // 改变每页条数
const register = () => {
state.formData = {
name: "",
describe: "",
state: 1,
p_dict_id: state.fatherRow ? state.fatherRow.dict_id : "",
};
if (state.bgForm) {
nextTick().then(() => {
state.bgForm.validate((valid) => {
if (!valid) {
state.bgForm.clearValidate();
}
});
});
}
state.addType = 1;
state.addDialog = true;
}; // 新增字典按钮
const edit_row = (row) => {
axios
.get(`/apaas/system/v5/dictionary/${row.id}`)
.then((res) => {
if (res.data.code == 200) {
state.actionRow = res.data.data;
state.formData = {
name: state.actionRow.name,
describe: state.actionRow.describe,
state: state.actionRow.state,
p_dict_id: state.actionRow.p_dict_id,
};
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
if (state.bgForm) {
nextTick().then(() => {
state.bgForm.validate((valid) => {
if (!valid) {
state.bgForm.clearValidate();
}
});
});
}
state.addType = 2;
state.addDialog = true;
}; // 编辑按钮
const addConfirm = () => {
state.bgForm.validate((valid) => {
if (valid) {
if (state.addType == 1) {
// 新增
let params = {
classify_id: state.nodeClassifyId,
...state.formData,
};
axios.post(`/apaas/system/v5/dictionary/add`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.addDialog = false;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
} else {
// 编辑
let params = {
id: state.actionRow.id,
...state.formData,
};
axios.put(`/apaas/system/v5/dictionary/update`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.addDialog = false;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
}
}
});
}; // 确定新增/编辑
const delete_row = (row) => {
state.dialogDelete = true;
state.actionRow = row;
}; // 删除按钮
const deleteData = () => {
axios.delete(`/apaas/system/v5/dictionary/${state.actionRow.id}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.dialogDelete = false;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
}; // 确定删除
const moveRow = (row, type) => {
let index;
state.tableRows.forEach((e, i) => {
if (e.id == row.id) {
index = i;
}
});
let nextRow;
if (type == 1) {
// 上移
nextRow = state.tableRows[index - 1];
} else {
// 下移
nextRow = state.tableRows[index + 1];
}
let params = [
{
id: row.id,
sort: nextRow.sort,
},
{
id: nextRow.id,
sort: row.sort,
},
];
axios
.put(`/apaas/system/v5/dictionary/sort`, [...params])
.then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
changePage(1);
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
onBeforeMount(() => {
getTypeList();
});
const {
typeList,
typeKeyword,
nodeClassifyId,
tableRows,
tableTotal,
filter,
stateOptions,
dialogDelete,
addType,
addDialog,
formData,
rules,
} = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px;
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.flex_row {
// flex-grow: 1;
width: 100%;
display: flex;
margin-bottom: 16px;
position: relative;
height: calc(100% - 62px);
.flex_left {
width: 240px;
background-color: #fff;
margin-right: 16px;
border-radius: 6px;
padding: 16px;
height: 100%;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
overflow: hidden;
.box {
width: 208px;
height: 100%;
overflow: hidden;
.type_station {
width: 100%;
margin-top: 16px;
height: calc(100% - 50px);
.type-box {
height: 32px;
line-height: 32px;
text-align: center;
font-size: 14px;
color: #404a62;
cursor: pointer;
&:hover {
background-color: #f2f3f7;
}
}
.current-type {
background-color: #f2f3f7;
border-radius: 4px;
}
}
}
}
.flex_right {
flex: 1;
background-color: #fff;
border-radius: 6px;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
height: 100%;
.main_container {
height: 100%;
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
height: calc(100% - 70px);
width: 100%;
padding: 0 16px;
.table {
max-height: calc(100% - 64px);
}
}
}
}
}
.bg_form {
width: 100%;
box-sizing: border-box;
.el-form-item {
margin-bottom: 24px;
:deep().el-form-item__label {
line-height: 36px;
height: 36px;
}
.el-form-item__content {
width: 100%;
.el-textarea {
:deep().el-input__count {
bottom: -16px;
right: 4px;
font-family: Roboto-Regular;
color: #a9b1c7;
}
}
.bg-switch-ele {
width: 52px;
height: 24px;
:deep().el-switch__core {
width: 100%;
height: 24px;
.el-switch__inner,
.el-switch__action {
top: 3px;
}
}
}
}
}
}
}
</style>
<!-- 角色管理 -->
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="form_content apaas_scroll_nor">
<el-form ref="refForm" :model="formData" :rules="rules" label-width="96px" class="registerForm">
<el-form-item label="LOGO" prop="logoUrl">
<bg-upload-image
v-model="formData.logoUrl"
:showTips="true"
customTips="请选择图片上传:支持jpg、png等格式,图片需小于500KB"
:fileSize="500"
:fileSizeUnit="'KB'"
:limit="1"
listType="picture-card"
@change="changeLogo"
:accept="['.jpg', '.jpeg', '.png']"></bg-upload-image>
</el-form-item>
<el-form-item label="登录背景图" prop="backgroundUrl">
<bg-upload-image
v-model="formData.backgroundUrl"
:fileSize="2"
:showTips="true"
customTips="请选择图片上传:大小1920 * 1026像素支持jpg、png等格式,图片需小于2MB"
:limit="1"
listType="picture-card"
@change="changeBanner"
:accept="['.jpg', '.jpeg', '.png']"></bg-upload-image>
</el-form-item>
<bg-form-gap title="底部所有权内容"></bg-form-gap>
<el-form-item label="内容" prop="patent">
<el-input
v-model.trim="formData.patent"
placeholder="例如:Copyright © 某某大数据, All Rights Reserved."></el-input>
</el-form-item>
<bg-form-gap title="底部ICP备案"></bg-form-gap>
<el-form-item label="内容" prop="icpFiling">
<el-input placeholder="请输入" v-model.trim="formData.icpFiling"></el-input>
</el-form-item>
<el-form-item label="跳转连接" prop="icpUrl">
<el-input placeholder="例如:https://...或http://..." v-model.trim="formData.icpUrl"></el-input>
</el-form-item>
<bg-form-gap title="底部公安网备案"></bg-form-gap>
<el-form-item label="内容" prop="publicSecurityFiling">
<el-input placeholder="请输入" v-model.trim="formData.publicSecurityFiling"></el-input>
</el-form-item>
<el-form-item label="跳转连接" prop="publicSecurityUrl">
<el-input placeholder="例如:https://...或http://..." v-model.trim="formData.publicSecurityUrl"></el-input>
</el-form-item>
<bg-form-gap title="法律声明"></bg-form-gap>
<el-form-item label="跳转连接" prop="legalStatementUrl">
<el-input placeholder="例如:https://...或http://..." v-model.trim="formData.legalStatementUrl"></el-input>
</el-form-item>
<bg-form-gap title="隐私政策"></bg-form-gap>
<el-form-item label="跳转连接" prop="privacyPolicyUrl">
<el-input placeholder="例如:https://...或http://..." v-model.trim="formData.privacyPolicyUrl"></el-input>
</el-form-item>
</el-form>
</div>
<div class="operate_btns">
<el-button type="primary" @click="save"> 保存 </el-button>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import { validateLink } from "@/services/rules.js";
import store from "../../../../store";
const refForm = ref(null);
const state = reactive({
formData: {
logoUrl: [],
backgroundUrl: [],
patent: "",
icpFiling: "",
icpUrl: "",
publicSecurityFiling: "",
publicSecurityUrl: "",
legalStatementUrl: "",
privacyPolicyUrl: "",
}, //表单数据
rules: {
logoUrl: [{ required: true, message: "请上传Logo", trigger: "change" }],
backgroundUrl: [{ required: true, message: "请上传背景图", trigger: "change" }],
icpUrl: [{ validator: validateLink, trigger: "blur" }],
publicSecurityUrl: [{ validator: validateLink, trigger: "blur" }],
legalStatementUrl: [{ validator: validateLink, trigger: "blur" }],
privacyPolicyUrl: [{ validator: validateLink, trigger: "blur" }],
}, //表单校验规则
});
const changeLogo = (value) => {
if (value && value.length > 0) {
refForm.value.clearValidate("logoUrl");
}
};
const changeBanner = (value) => {
if (value && value.length > 0) {
refForm.value.clearValidate("backgroundUrl");
}
};
const save = () => {
refForm.value.validate((valid) => {
if (valid) {
let params = {
...state.formData,
};
params.logoUrl = JSON.stringify(params.logoUrl);
params.backgroundUrl = JSON.stringify(params.backgroundUrl);
axios.put(`/apaas/system/v5/login/updateLogin`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getLoginPageConfig();
} else {
ElMessage.error(res.data.data);
}
});
} else {
ElMessage.error("请先将表单填写完整");
}
});
};
const getLoginPageConfig = () => {
axios.get(`/apaas/system/v5/login/loginDetail`).then((res) => {
if (res.data.code == 200) {
let data = res.data.data;
store.commit("setSystemLogo", data.logoUrl);
data.logoUrl = data.logoUrl ? JSON.parse(data.logoUrl) : [];
data.backgroundUrl = data.backgroundUrl ? JSON.parse(data.backgroundUrl) : [];
Object.assign(state.formData, data);
} else {
ElMessage.error(res.data.data);
}
});
};
onBeforeMount(() => {
getLoginPageConfig();
});
const { formData, rules } = toRefs(state);
</script>
<style lang="scss" scoped>
.page_container {
.main_container {
position: relative;
margin: 0 0 16px;
width: 100%;
height: calc(100% - 62px);
padding: 32px 0 70px;
overflow: auto;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
.form_content {
overflow: auto;
height: 100%;
padding-left: 24px;
.el-form {
width: 60%;
:deep() .el-form-item {
&:nth-of-type(1) {
.el-upload--picture-card {
width: 120px;
height: 120px;
}
}
&:nth-of-type(2) {
.el-upload--picture-card {
width: 367px;
height: 196px;
}
}
}
}
:deep() .gap-title {
font-size: 16px;
color: #202531;
}
}
.operate_btns {
position: absolute;
bottom: 0;
width: 100%;
height: 70px;
text-align: right;
line-height: 68px;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
margin-right: 16px;
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="parent_container">
<div class="left_container bgc_white">
<el-menu default-active="1" @select="switchMenu">
<el-menu-item index="1">
<span>授权信息</span>
</el-menu-item>
<el-menu-item index="2">
<span>安全相关</span>
</el-menu-item>
<el-menu-item index="3">
<span>登录注册配置</span>
</el-menu-item>
</el-menu>
</div>
<div class="right_container bgc_white">
<!-- 授权信息表单 -->
<el-form
v-if="activeIndex == 1"
ref="authorizeFormRef"
:model="authorizeFormData"
:rules="authorizeRules"
label-width="110px"
class="authorizeForm">
<el-form-item label="当前版本号" prop="system_version">
<el-input disabled v-model="authorizeFormData.system_version" />
</el-form-item>
<el-form-item label="User" prop="system_user">
<el-input placeholder="统一管理用户认证平台" v-model="authorizeFormData.system_user" maxlength="30" />
</el-form-item>
<el-form-item label="Key" prop="license">
<el-input
placeholder="请输入"
rows="5"
v-model="authorizeFormData.license"
type="textarea"
@blur="getLicenseInfo" />
</el-form-item>
<el-form-item label="有效期至" prop="license_dead_date">
<el-input disabled v-model="authorizeFormData.license_dead_date" />
</el-form-item>
<el-form-item label="剩余有效期提醒" prop="license_inform_day">
<el-input placeholder="请输入" v-model.trim.number="authorizeFormData.license_inform_day">
<template #append></template>
</el-input>
<el-tooltip placement="top-start" effect="light">
<template #content>
范围(0~90),单位:天, 当剩余有效期在配置区间内时,系统会在用户登录系统时发出提醒,0表示不提醒
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
</el-form>
<!-- 安全相关表单 -->
<el-form
v-if="activeIndex == 2"
ref="secureFormRef"
:model="secureFormData"
:rules="secureRules"
label-width="110px"
class="secureForm">
<div class="passwordItem">
<el-form-item label="最低密码强度" prop="min_pwd_level">
<el-select v-model="secureFormData.min_pwd_level" placeholder="请选择">
<el-option label="弱" :value="1" />
<el-option label="中" :value="2" />
<el-option label="强" :value="3" />
</el-select>
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>支持大写字母/小写字符/数字/特殊符号</span>
<br />
<span>密码长度应至少8位字符</span>
<br />
<span>弱:至少包含一种组合。</span>
<br />
<span>中:至少包含两种组合。</span>
<br />
<span>强:至少包含三种及以上组合。</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
<el-form-item label="是否强制修改" prop="force_update_state">
<bg-switch
:labels="['否', '是']"
:values="[0, 1]"
v-model="secureFormData.force_update_state"></bg-switch>
</el-form-item>
</div>
<el-form-item label="密码有效期" prop="pwd_validity">
<el-input placeholder="请输入" v-model.trim.number="secureFormData.pwd_validity" maxlength="3">
<template #append></template>
</el-input>
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>单位,天</span>
<br />
<span>密码有效期是指强制修改密码的周期,例如60天表示用户需要每60天修改一次密码。</span>
<br />
<span>若配置为0,表示密码有效期为永久</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
<el-form-item label="会话有效期" prop="session_validity">
<el-input placeholder="请输入" v-model.trim.number="secureFormData.session_validity">
<template #append>分钟</template>
</el-input>
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>{{ `范围(10~${session_validity_max})` }}</span>
<br />
<span>单位,分钟</span>
<br />
<span>会话有效期是指您登录后,一定时间内无操作,系统将自动登出</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
<el-form-item label="访问规则模式" prop="access_rule_state">
<el-select placeholder="请选择" v-model="secureFormData.access_rule_state">
<el-option label="关闭" :value="0"> </el-option>
<el-option label="打开" :value="1"> </el-option>
</el-select>
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>访问规则模式开启后,按照【访问规则管理】配置执行。</span>
<br />
<span>该模式针对超管无效</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</el-form-item>
</el-form>
<!-- 登录注册配置表单 -->
<el-form
v-if="activeIndex == 3"
ref="registerFormRef"
:model="registerFormData"
:rules="registerRules"
label-width="125px"
class="registerForm">
<bg-form-gap title="登录受限配置"></bg-form-gap>
<el-form-item label="登录受限是否启用" prop="login_config_state">
<bg-switch
:labels="['否', '是']"
:values="[0, 1]"
v-model="registerFormData.login_config_state"></bg-switch>
</el-form-item>
<div class="input_password_config" v-if="registerFormData.login_config_state == 1">
<span></span>
<el-form-item prop="login_limit_time" label-width="0">
<el-input
placeholder="请输入小时数"
v-model.trim.number="registerFormData.login_limit_time"
maxlength="3"></el-input>
</el-form-item>
<span>小时内</span>
<el-form-item prop="login_pwd_error" label-width="0">
<el-input
placeholder="请输入密码输错次数"
v-model.trim.number="registerFormData.login_pwd_error"
maxlength="3"></el-input>
</el-form-item>
<span>次输错密码,自动锁定</span>
<el-form-item prop="login_lock_time" label-width="0">
<el-input
placeholder="请输入分钟数"
v-model.trim.number="registerFormData.login_lock_time"
maxlength="3"></el-input>
</el-form-item>
<span>分钟</span>
<el-button type="primary" @click="restoreDialog = true">一键还原受限</el-button>
<el-tooltip placement="top-start" effect="light">
<template #content>
<span>必须为大于0的正整数且不能为空</span>
<br />
<span>一键还原按钮将所有锁定的账号解锁并将输错次数还原</span>
</template>
<div class="tip-image"></div>
</el-tooltip>
</div>
<bg-form-gap title="注册配置"></bg-form-gap>
<el-form-item label="允许注册" prop="register_config_state">
<el-radio-group v-model="registerFormData.register_config_state">
<el-radio :label="1"></el-radio>
<el-radio :label="0"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div class="operate_btns">
<el-button type="primary" @click="save"> 保存 </el-button>
</div>
</div>
</div>
<el-dialog class="dialog_box" title="提示" v-model="restoreDialog" width="420px">
<div>该操作将还原所有锁定账号和输错次数,是否继续?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="restoreDialog = false"> </el-button>
<el-button type="primary" @click="confirm"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, onBeforeMount, ref } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import { ElMessage } from "element-plus";
import axios from "@/request/http.js";
const session_validity_max = window.defaultConfig.session_validity_max;
const authorizeFormRef = ref(null);
const secureFormRef = ref(null);
const registerFormRef = ref(null);
const state = reactive({
//授权信息表单
authorizeFormData: {
id: "",
system_version: "",
system_user: "",
license: "",
license_dead_date: "",
license_inform_day: "",
},
//授权表单校验规则
authorizeRules: {
license_inform_day: [{ type: "number", min: 0, max: 90, message: "请输入0~90的整数", trigger: "blur" }],
},
//安全相关信息表单
secureFormData: {
id: "",
min_pwd_level: "",
force_update_state: 0,
pwd_validity: "",
session_validity: "",
access_rule_state: "",
},
//安全相关表单校验规则
secureRules: {
pwd_validity: [
{
type: "number",
message: "请输入大于或等于0的整数",
trigger: "blur",
},
],
session_validity: [
{
type: "number",
min: 10,
max: session_validity_max,
message: `请输入10~${session_validity_max}的整数`,
trigger: "blur",
},
],
},
//登录注册配置表单
registerFormData: {
id: "",
login_config_state: 0,
login_limit_time: "",
login_pwd_error: "",
login_lock_time: "",
register_config_state: 0,
},
//登录注册配置表单校验规则
registerRules: {
login_limit_time: [
{
type: "number",
min: 1,
message: "请输入正整数",
trigger: "blur",
},
],
login_pwd_error: [
{
type: "number",
min: 1,
message: "请输入正整数",
trigger: "blur",
},
],
login_lock_time: [
{
type: "number",
min: 1,
message: "请输入正整数",
trigger: "blur",
},
],
},
//选中项索引
activeIndex: 1,
restoreDialog: false,
});
const {
authorizeFormData,
authorizeRules,
secureFormData,
secureRules,
registerFormData,
registerRules,
activeIndex,
restoreDialog,
} = toRefs(state);
onBeforeMount(() => {
getPreferenceConfig();
});
//切换菜单选项
const switchMenu = (index) => {
state.activeIndex = index;
};
//获取首选项配置
const getPreferenceConfig = () => {
axios.get("/apaas/system/v5/sysOptions").then((res) => {
if (res.data.code == 200) {
let data = res.data.data;
Object.keys(state.authorizeFormData).forEach((item) => {
state.authorizeFormData[item] = data[item];
});
Object.keys(state.secureFormData).forEach((item) => {
state.secureFormData[item] = data[item];
});
Object.keys(state.registerFormData).forEach((item) => {
state.registerFormData[item] = data[item];
});
} else {
ElMessage.error(res.data.data);
}
});
};
//根据license获取授权信息
const getLicenseInfo = () => {
if (!state.authorizeFormData.license) return;
axios
.post("/apaas/system/v5/sysOptions/getLicenseInfo", {
license: state.authorizeFormData.license,
})
.then((res) => {
if (res.data.code == 200) {
const data = res.data.data || {};
state.authorizeFormData.system_version = data.version;
state.authorizeFormData.license_dead_date = data.deadDate;
} else {
ElMessage.error(res.data.data);
}
});
};
//保存表单项
const save = () => {
let params = {};
if (state.activeIndex == 1) {
authorizeFormRef.value.validate((valid) => {
if (valid) {
params = {
...state.authorizeFormData,
};
postData("/apaas/system/v5/sysOptions/licenseOpts", params);
}
});
} else if (state.activeIndex == 2) {
secureFormRef.value.validate((valid) => {
if (valid) {
params = {
...state.secureFormData,
};
postData("/apaas/system/v5/sysOptions/safeOpts", params);
}
});
} else {
registerFormRef.value.validate((valid) => {
if (valid) {
params = {
...state.registerFormData,
};
if (params.login_config_state == 0) {
params.login_limit_time = 0;
params.login_pwd_error = 0;
params.login_lock_time = 0;
}
postData("/apaas/system/v5/sysOptions/loginOpts", params);
}
});
}
};
// 请求接口发送消息
const postData = (url, params) => {
axios.post(url, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
});
};
const confirm = () => {
axios.post(`/apaas/system/v5/sysOptions/reStartConfig`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.restoreDialog = false;
} else {
ElMessage.error(res.data.data);
}
});
};
</script>
<style lang="scss" scoped>
.parent_container {
display: flex;
justify-content: space-between;
height: calc(100% - 46px - 20px);
.left_container {
width: 240px;
height: 100%;
padding: 24px 16px 0;
.el-menu {
border: none;
.el-menu-item {
justify-content: center;
height: 36px;
color: #404a62 !important;
&.is-active,
&:hover {
background-color: #f2f3f7;
border-radius: 4px;
}
}
}
}
.right_container {
position: relative;
width: calc(100% - 250px);
min-width: 852px;
height: 100%;
padding: 32px 0 68px 24px;
margin-left: 16px;
.authorizeForm,
.secureForm,
.registerForm {
:deep() .el-form-item {
margin-bottom: 24px;
width: 54%;
min-width: 580px;
font-size: 14px;
.el-form-item__label,
.el-radio__label {
color: var(--el-text-color-primary);
}
.el-select {
width: 100%;
}
.el-input-group__append {
width: 60px;
color: #404a62;
border-radius: 0 6px 6px 0;
background: #f7f7f9;
}
}
}
.secureForm {
.passwordItem {
display: flex;
align-items: center;
.el-form-item:nth-of-type(1) {
flex-shrink: 0;
margin-right: 56px;
}
.el-form-item:nth-of-type(2) {
min-width: auto;
}
}
}
.registerForm {
.input_password_config {
position: relative;
display: flex;
align-items: center;
width: 798px;
margin-bottom: 24px;
font-size: 14px;
color: var(--el-text-color-primary);
.el-form-item {
margin: 0px 8px;
min-width: auto;
&:nth-of-type(1),
&:nth-of-type(3) {
flex: 0;
min-width: 110px;
width: 120px;
}
&:nth-of-type(2) {
flex: 0;
min-width: 140px;
width: 150px;
}
}
.el-button {
margin-left: 18px;
}
}
.el-radio {
margin-right: 54px;
}
}
.operate_btns {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 68px;
padding-right: 16px;
line-height: 68px;
text-align: right;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
}
}
}
}
</style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="switch-box">
<div class="switch-label">是否启用</div>
<!-- 特别说明,本来这个开关适用于整个功能,但是经沟通,后端的欢迎语开关也是唯一的,他们决定用这个欢迎语开关来确认整个功能的是否启用,所以代码逻辑可能产生误会,特此说明 -->
<bg-switch
@click="handleWelcomeSwitch()"
class="bg-switch-ele"
v-model="welcome.state"
:values="[0, 1]"
inline-prompt
:labels="['否', '是']" />
</div>
<div class="welcome-box">
<div class="welcome-label"><span class="required">*</span>欢迎语</div>
<div class="welcome-form">
<el-input
type="text"
maxlength="50"
v-model="welcome.content"
placeholder="请输入欢迎语"
show-word-limit></el-input>
<el-button type="primary" @click="setWelcomeText">保存</el-button>
</div>
</div>
<div class="question-box">
<div>问题分类</div>
<el-button type="primary" @click="handleCategoryUpdate('create')">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
<div class="table_container" style="padding: 0">
<div class="table bg-scroll">
<bg-table ref="bgTable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true">
<template #state="{ row }">
<bg-switch
@click="changeUseRow(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.state"></bg-switch>
</template>
<template #problem_number="{ row }">
<router-link :to="`/config/reply-settings/question-category?id=${row.id}`">{{
row.problem_number
}}</router-link>
</template>
<template #action="{ row }">
<bg-table-btn @click="$router.push(`/config/reply-settings/question-category?id=${row.id}`)"
>添加问题</bg-table-btn
>
<bg-table-btn @click="handleCategoryUpdate('edit', row)">编辑</bg-table-btn>
<bg-table-btn @click="handleDeleteDialog(row)">删除</bg-table-btn>
<bg-table-btn @click="moveRow(row, 1)" :disabled="!row.canMoveUp">上移</bg-table-btn>
<bg-table-btn @click="moveRow(row, 2)" :disabled="!row.canMoveDown">下移</bg-table-btn>
</template>
</bg-table>
</div>
<bg-pagination
:page="filter.page"
:size="filter.size"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
</div>
<el-dialog
class="dialog_box"
:title="categoryEditType == 'create' ? '新增' : '编辑'"
v-model="dialogCategoryEdit"
width="614px">
<el-form ref="bgForm" :model="ruleForm" :rules="rules" label-width="80px" class="bg_form">
<el-form-item label="描述" prop="categoryName">
<el-input
v-model="ruleForm.categoryName"
type="text"
show-word-limit
maxlength="50"
placeholder="请输入问题分类"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="apaas_button">
<el-button type="default" @click="dialogCategoryEdit = false">取消</el-button>
<el-button type="primary" @click="handleSaveCategory(bgForm)">保存</el-button>
</div>
</template>
</el-dialog>
<el-dialog
class="dialog_box"
title="提示"
v-model="dialogDelete"
width="400px"
:before-close="
() => {
dialogDelete = false;
}
">
<div>确定要删除吗?</div>
<template #footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const bgForm = ref(null);
const state = reactive({
welcome: {},
tableRows: [],
headers: [
{
label: "问题分类",
prop: "problem_type",
minWidth: 360,
},
{
label: "问题数量",
prop: "problem_number",
},
{
label: "是否启用",
prop: "state",
},
{
label: "操作",
prop: "action",
width: 340,
fixed: "right",
},
],
filter: {
page: 1,
limit: 10,
},
categoryEditType: "",
dialogCategoryEdit: false,
ruleForm: {
categoryName: "",
},
rules: {
categoryName: [{ required: true, message: "请输入问题分类", trigger: "blur" }],
},
dialogDelete: false,
actionRow: null,
});
const getTableRows = () => {
let params = { ...state.filter };
axios
.get(`/apaas/system/v5/customer/service/problem/type`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
if (state.tableRows.length > 0) {
state.tableRows.forEach((e) => {
e.canMoveUp = true;
e.canMoveDown = true;
});
state.tableRows[0].canMoveUp = false;
state.tableRows[state.tableRows.length - 1].canMoveDown = false;
}
} else {
ElMessage.error(res.data.data);
}
});
};
const moveRow = (row, type) => {
let index;
state.tableRows.forEach((e, i) => {
if (e.id == row.id) {
index = i;
}
});
let nextRow;
if (type == 1) {
// 上移
nextRow = state.tableRows[index - 1];
} else {
// 下移
nextRow = state.tableRows[index + 1];
}
let params = [
{
id: row.id,
sort: nextRow.sort,
},
{
id: nextRow.id,
sort: row.sort,
},
];
axios
.post(`/apaas/system/v5/customer/service/problem/type/sort`, [...params])
.then((res) => {
if (res.data.code == 200) {
ElMessage.success("操作成功");
changePage(1);
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const changeUseRow = (row) => {
axios
.put(`/apaas/system/v5/customer/service/problem/type/state`, {
id: row.id,
state: row.state,
})
.then((res) => {
if (res.data.code == 200) {
ElMessage.success("操作成功");
changePage(1);
} else {
ElMessage.error(res.data.data);
row.state = row.state == 0 ? 1 : 0;
}
});
}; // 启用禁用
const changePage = (page) => {
state.filter.page = page;
getTableRows();
}; // 改变页码
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
}; // 改变每页条数
const handleCategoryUpdate = (type, row) => {
state.dialogCategoryEdit = true;
state.categoryEditType = type;
if (type !== "create") {
state.actionRow = row;
state.ruleForm.categoryName = row.problem_type;
}
};
const handleSaveCategory = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
const isNew = state.categoryEditType === "create";
axios
.post("/apaas/system/v5/customer/service/problem/type", {
id: isNew ? 0 : 1,
problem_type: state.ruleForm.categoryName,
state: isNew ? 0 : state.actionRow.state,
})
.then(({ data }) => {
if (data.code === 200) {
ElMessage.success("添加成功");
changePage(1);
state.dialogCategoryEdit = false;
state.ruleForm.categoryName = "";
} else {
ElMessage.error(data.data);
}
});
} else {
console.log("error submit!", fields);
}
});
};
const handleDeleteDialog = (row) => {
state.actionRow = row;
state.dialogDelete = true;
};
const deleteData = () => {
axios.delete(`/apaas/system/v5/customer/service/problem/type/${state.actionRow.id}`).then(({ data }) => {
if (data.code === 200) {
ElMessage.success("操作成功");
changePage(1);
state.dialogDelete = false;
} else {
ElMessage.error(data.data);
}
});
};
const handleWelcomeSwitch = () => {
// 可能会造成的bug,如果用户改了欢迎语,但是不点保存,直接点击开关,也会影响到欢迎语的变化,所以千万不要修改欢迎语不保存,去点击开关。
axios.post("/apaas/system/v5/customer/service/welcome", state.welcome).then(({ data }) => {
if (data.code === 200) {
ElMessage.success("操作成功");
} else {
ElMessage.error(data.data);
}
});
};
const getWelcomeText = () => {
axios.get("/apaas/system/v5/customer/service/welcome").then(({ data }) => {
if (data.data) {
state.welcome = {
state: data.data.state,
id: data.data.id,
content: data.data.content,
};
} else {
state.welcome = {
content: "",
state: 1,
};
}
});
};
const setWelcomeText = () => {
axios.post("/apaas/system/v5/customer/service/welcome", state.welcome).then(({ data }) => {
if (data.code === 200) {
ElMessage.success("操作成功");
} else {
ElMessage.error(data.data);
}
});
};
onMounted(() => {
changePage(1);
getWelcomeText();
});
const {
welcome,
ruleForm,
rules,
categoryEditType,
dialogCategoryEdit,
headers,
dialogDelete,
filter,
tableRows,
tableTotal,
} = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
font-size: 14px;
.main_container {
padding: 40px;
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
.table_container {
height: calc(100% - 70px);
padding: 0 16px;
.table {
max-height: calc(100% - 64px);
}
}
}
}
.switch-box {
display: flex;
align-items: center;
.switch-label {
margin-right: 17px;
}
.bg-switch-ele {
}
}
.welcome-box {
margin-top: 23px;
.welcome-label .required {
color: #d75138;
}
.welcome-form {
margin-top: 7px;
.el-input {
width: 994px;
margin-right: 8px;
}
}
}
.question-box {
margin-top: 42px;
height: 100%;
.el-button {
margin: 23px 0 8px;
}
}
</style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="question-box">
<div>问题分类 - {{ category.problem_type }}{{ category.problem_number }}/8)</div>
<el-button type="primary" @click="handleQuestionUpdate('create')">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
<div class="table_container" style="padding: 0">
<div class="table bg-scroll">
<bg-table ref="bgTable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true">
<template #action="{ row }">
<bg-table-btn @click="handleQuestionUpdate('edit', row)">编辑</bg-table-btn>
<bg-table-btn @click="handleDeleteDialog(row)">删除</bg-table-btn>
<bg-table-btn @click="moveRow(row, 1)" :disabled="!row.canMoveUp">上移</bg-table-btn>
<bg-table-btn @click="moveRow(row, 2)" :disabled="!row.canMoveDown">下移</bg-table-btn>
</template>
</bg-table>
</div>
<!-- <bg-pagination
:page="filter.page"
:size="filter.size"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination> -->
</div>
</div>
</div>
<el-dialog
class="dialog_box"
:title="questionEditType == 'create' ? '新增' : '编辑'"
v-model="dialogQuestionEdit"
width="614px">
<el-form ref="bgForm" :model="ruleForm" :rules="rules" label-width="80px" class="bg_form">
<el-form-item label="标题" prop="title">
<el-input
v-model="ruleForm.title"
type="text"
show-word-limit
maxlength="50"
placeholder="请输入标题"></el-input>
</el-form-item>
<el-form-item label="回复内容" prop="content">
<el-input
v-model="ruleForm.content"
type="textarea"
:rows="8"
show-word-limit
maxlength="500"
placeholder="请输入回复内容"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="apaas_button">
<el-button type="default" @click="dialogQuestionEdit = false">取消</el-button>
<el-button type="primary" @click="handleSaveCategory(bgForm)">保存</el-button>
</div>
</template>
</el-dialog>
<el-dialog
class="dialog_box"
title="提示"
v-model="dialogDelete"
width="400px"
:before-close="
() => {
dialogDelete = false;
}
">
<div>确定要删除吗?</div>
<template #footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { onMounted, reactive, toRefs, ref } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import { useRoute } from "vue-router";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const route = useRoute();
const { id } = route.query;
const bgForm = ref(null);
const state = reactive({
category: {},
tableRows: [],
// filter: {
// page: 1,
// limit: 10,
// },
headers: [
{
label: "标题",
prop: "title",
width: 180,
},
{
label: "回复内容",
prop: "content",
},
{
label: "操作",
prop: "action",
width: 260,
fixed: "right",
},
],
questionEditType: "",
dialogQuestionEdit: false,
ruleForm: {
title: "",
content: "",
},
rules: {
title: [{ required: true, message: "请输入标题", trigger: "blur" }],
content: [{ required: true, message: "请输入回复内容", trigger: "blur" }],
},
dialogDelete: false,
actionRow: null,
});
const handleDeleteDialog = (row) => {
state.actionRow = row;
state.dialogDelete = true;
};
const deleteData = () => {
axios.delete(`/apaas/system/v5/customer/service/problem/${state.actionRow.id}`).then(({ data }) => {
if (data.code === 200) {
ElMessage.success("操作成功");
getCategoryWithQuestions();
state.dialogDelete = false;
} else {
ElMessage.error(data.data);
}
});
};
const handleQuestionUpdate = (type, row) => {
state.dialogQuestionEdit = true;
state.questionEditType = type;
if (type !== "create") {
state.actionRow = row;
state.ruleForm.title = row.problem_type;
}
};
const handleSaveCategory = async (formEl) => {
if (!formEl) return;
await formEl.validate((valid, fields) => {
if (valid) {
const isNew = state.questionEditType === "create";
axios
.post(
"/apaas/system/v5/customer/service/problem",
Object.assign(
{
problem_type_id: state.category.problem_type_id,
title: state.ruleForm.title,
content: state.ruleForm.content,
},
!isNew && { id: actionRow.id }
)
)
.then(({ data }) => {
if (data.code === 200) {
getCategoryWithQuestions();
ElMessage.success("添加成功");
state.dialogQuestionEdit = false;
state.ruleForm.title = "";
state.ruleForm.content = "";
} else {
ElMessage.error(data.data);
}
});
} else {
console.log("error submit!", fields);
}
});
};
const getCategoryList = () => {
axios.get(`/apaas/system/v5/customer/service/problem/type`).then((res) => {
if (res.data.code == 200) {
const [category] = res.data.data.filter((item) => item.id.toString() === id);
state.category = category;
} else {
ElMessage.error(res.data.data);
}
});
};
const getQuestionList = () => {
axios.get(`/apaas/system/v5/customer/service/problem/${id}`).then(({ data }) => {
state.tableRows = data.data || [];
if (state.tableRows.length > 0) {
state.tableRows.forEach((e) => {
e.canMoveUp = true;
e.canMoveDown = true;
});
state.tableRows[0].canMoveUp = false;
state.tableRows[state.tableRows.length - 1].canMoveDown = false;
}
});
};
const moveRow = (row, type) => {
let index;
state.tableRows.forEach((e, i) => {
if (e.id == row.id) {
index = i;
}
});
let nextRow;
if (type == 1) {
// 上移
nextRow = state.tableRows[index - 1];
} else {
// 下移
nextRow = state.tableRows[index + 1];
}
let params = [
{
id: row.id,
sort: nextRow.sort,
},
{
id: nextRow.id,
sort: row.sort,
},
];
axios
.post(`/apaas/system/v5/customer/service/problem/sort`, [...params])
.then((res) => {
if (res.data.code == 200) {
ElMessage.success("操作成功");
getCategoryWithQuestions();
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const getCategoryWithQuestions = () => {
getCategoryList();
getQuestionList();
};
onMounted(() => {
getCategoryWithQuestions();
});
const { category, tableRows, headers, ruleForm, rules, questionEditType, dialogQuestionEdit, dialogDelete } =
toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
font-size: 14px;
.main_container {
padding: 40px;
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
.table_container {
height: calc(100% - 70px);
padding: 0 16px;
.table {
max-height: calc(100% - 64px);
}
}
}
}
.question-box {
height: 100%;
.el-button {
margin: 40px 0 8px;
}
}
:deep(.el-dialog__body) {
padding-bottom: 0;
}
</style>
<template>
<el-form
:label-position="'right'"
label-width="120px"
:model="contactForm"
:rules="contactFormRules"
ref="contactRef"
style="max-width: 66%">
<el-form-item label="联系人姓名" prop="contact_name">
<el-input v-model="contactForm.contact_name" />
</el-form-item>
<el-form-item label="联系人手机号" prop="contact_phone">
<el-input v-model="contactForm.contact_phone" />
</el-form-item>
<el-form-item label="联系人邮箱" prop="contact_email">
<el-input v-model="contactForm.contact_email" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input type="textarea" :rows="3" v-model="contactForm.remark" show-word-limit maxlength="300" />
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref, onMounted } from "vue";
const contactForm = reactive({
contact_name: "",
contact_phone: "",
contact_email: "",
remark: "",
});
const props = defineProps({
formType: {
type: Boolean,
default: false, //false 新增 true 编辑
},
id: {
type: String,
default: "",
},
});
const checkPhone = (rule, value, callback) => {
var phone_ruler = /^(?:(?:\+|00)86)?1[3-9]\d{9}$/;
setTimeout(() => {
if (!phone_ruler.test(value) && value.length !== 0) {
callback(new Error("请输入正确的手机号码"));
} else {
callback();
}
});
};
const checkName = (rule, value, callback) => {
var reg = /^[a-zA-Z0-9\u4e00-\u9fa5]+$/;
if (!reg.test(value)) {
callback(new Error("只能输入字母、数字和汉字"));
} else {
callback();
}
};
const contactFormRules = reactive({
contact_name: [
{ required: true, message: "请输入联系人姓名", trigger: "blur" },
{ max: 50, message: "联系人姓名最大长度为50个字符", trigger: "blur" },
{ validator: checkName, trigger: "blur" },
],
contact_phone: [
{ required: true, message: "请输入联系人手机号", trigger: "blur" },
{ validator: checkPhone, trigger: "blur" },
],
contact_email: [
{ type: "email", message: "请输入正确的邮箱", trigger: "blur" },
{ max: 100, message: "邮箱最大长度为100个字符", trigger: "blur" },
],
});
const contactRef = ref(null);
const emit = defineEmits(["action"]);
const submitForm = async () => {
if (!contactRef) return;
await contactRef.value.validate((valid, fields) => {
if (valid) {
emit("action", contactForm);
} else {
emit("action", null);
}
});
};
const clearForm = () => {
if (!contactRef) return;
contactRef.value.resetFields();
};
const setForm = (data) => {
Object.assign(contactForm, data);
};
onMounted(() => {});
defineExpose({ submitForm, clearForm, setForm });
</script>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="page_content flex_cloumn">
<div class="content_top apaas_scroll">
<div class="content_process">
<div>
<el-steps :active="step">
<el-step title="" :class="{ process_complete: step > 1 }">
<template #icon>
<span class="process_desc">
<span class="icon_box" v-if="step > 0"
><bg-icon class="step_icon" icon="#bg-ic-file"></bg-icon
></span>
<span class="circle" v-else></span>
业务系统信息
</span>
</template>
</el-step>
<el-step title="" :class="{ process_complete: step > 2 }">
<template #icon>
<span class="process_desc">
<span class="icon_box" v-if="step > 1"
><bg-icon class="step_icon" icon="#bg-ic-file-staff"></bg-icon
></span>
<span class="circle" v-else></span>
联系人信息
</span>
</template>
</el-step>
<el-step title="">
<template #icon>
<span class="process_desc">
<span class="icon_box" v-if="step > 2"
><bg-icon class="step_icon" icon="#bg-ic-file-success"></bg-icon
></span>
<span class="circle" v-else></span>
完成
</span>
</template>
</el-step>
</el-steps>
</div>
</div>
<div class="content_main">
<systemForm
v-show="step === 1"
ref="systemFormRef"
:form-type="route.query.id ? true : false"
:id="route.query.id"
@action="getSystemFormData"></systemForm>
<contactForm
v-show="step === 2"
ref="contactFormRef"
:id="route.query.id"
@action="getContactFormData"></contactForm>
<div class="process_end" v-show="step === 3">
<div>
<div>
<img v-if="successFlag" src="@/assets/imgs/img_data-complete.png" alt="" />
<img v-else src="@/assets/imgs/img_data-fail.png" alt="" />
</div>
<div class="font_bold">
{{
route.query.id
? successFlag
? "业务系统编辑成功"
: "业务系统编辑失败"
: successFlag
? "业务系统新增成功"
: "业务系统新增失败"
}}
</div>
<el-button @click="cancel">返回列表</el-button>
<el-button v-if="!route.query.id" type="primary" @click="continueCreate">继续创建</el-button>
</div>
</div>
</div>
</div>
<div class="content_bottom" v-if="step !== 3">
<div v-show="step === 1">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="nextStep">下一步</el-button>
</div>
<div v-show="step === 2">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="previousStep">上一步</el-button>
<el-button type="primary" @click="submit">保存</el-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { useRoute, useRouter } from "vue-router";
import systemForm from "./system-form.vue";
import contactForm from "./contact-form.vue";
import { reactive, ref, onBeforeMount, toRefs, computed, onBeforeUnmount, onMounted } from "vue";
import CryptoJS from "crypto-js";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const route = useRoute();
const router = useRouter();
const step = ref(1);
const systemFormRef = ref(null);
const contactFormRef = ref(null);
const formData = reactive({
organization_id: "",
business_code: "",
business_name: "",
business_desc: "",
logo: "",
system_role_id: "",
system_account: "",
system_phone: "",
password: "",
confirm_password: "",
access_address: "",
develop_id: "",
state: 1,
contact_name: "",
contact_phone: "",
contact_email: "",
remark: "",
}); //业务系统表单+联系人表单
const successFlag = ref(false);
// 下一步
const nextStep = () => {
systemFormRef.value.submitForm();
};
//业务系统表单检验触发事件 data为null 校验失败
const getSystemFormData = (data) => {
if (data) {
Object.assign(formData, data);
step.value = 2;
} else {
}
};
//联系人表单检验触发事件 data为null 校验失败
const getContactFormData = (data) => {
if (data) {
Object.assign(formData, data);
if (route.query.id) {
console.log(formData);
let params = {
...formData,
id: parseInt(route.query.id),
logo: formData.logo && formData.logo.length > 0 ? formData.logo[0].url : "",
};
axios.post(`/apaas/system/v5/user/update`, params).then((res) => {
if (res.data.code == 200) {
successFlag.value = true;
step.value = 3;
} else {
ElMessage.error(res.data.data);
}
});
} else {
let params = {
...formData,
logo: formData.logo && formData.logo.length > 0 ? formData.logo[0].url : "",
password: CryptoJS.AES.encrypt(formData.password, "swuE9cmCZQwrkYRV").toString(),
};
axios.put(`/apaas/system/v5/user/create`, params).then((res) => {
if (res.data.code == 200) {
successFlag.value = true;
step.value = 3;
} else {
ElMessage.error(res.data.data);
}
});
}
}
};
//上一步
const previousStep = () => {
step.value--;
};
//提交表单
const submit = () => {
contactFormRef.value.submitForm();
};
//继续创建 清空表单
const continueCreate = () => {
systemFormRef.value.clearForm();
contactFormRef.value.clearForm();
step.value = 1;
successFlag.value = false;
};
//取消
const cancel = () => {
// router.go(-1);
router.push({
path: "/develop/account",
query: {
id: formData.organization_id,
},
});
};
const getDetail = () => {
axios.get(`/apaas/system/v5/user/detail/${route.query.id}`).then((res) => {
if (res.data.code == 200) {
const form = res.data.data;
systemFormRef.value.setForm({
organization_id: form.organization_id,
system_role_id: form.system_role_id,
logo: form.logo ? [{ url: form.logo }] : [],
business_code: form.business_code,
business_name: form.business_name,
business_desc: form.business_desc,
system_account: form.system_account,
system_phone: form.system_phone,
access_address: form.access_address,
develop_id: form.develop_id,
state: form.state,
appid: form.app_id,
appsecret: form.app_secret,
});
contactFormRef.value.setForm({
contact_name: form.contact_name,
contact_phone: form.contact_phone,
contact_email: form.contact_email,
remark: form.remark,
});
} else {
ElMessage.error(res.data.data);
}
});
};
onBeforeUnmount(() => {});
onMounted(() => {
if (route.query.id) {
getDetail();
}
});
</script>
<style scoped>
.flex_cloumn {
display: flex;
flex-direction: column;
}
.content_top {
flex: 1;
display: flex;
flex-direction: column;
overflow: auto;
}
/* .content_top::-webkit-scrollbar {
width: 5px;
height: 0px;
}
.content_top::-webkit-scrollbar-thumb {
background: #dedede;
border-radius: 10px;
height: 0px;
}
.content_top::-webkit-scrollbar-track {
background: transparent;
border-radius: 2px;
} */
.content_bottom {
height: 68px;
line-height: 68px;
text-align: right;
padding: 0 20px;
border-top: 1px solid #e6e9ef;
}
.content_process {
border-bottom: 1px solid #e6e9ef;
padding: 38px 16%;
}
.content_main {
flex: 1;
padding: 30px;
box-sizing: border-box;
}
.process_desc {
background-color: #fff;
display: inline-block;
position: absolute;
padding: 0 5px;
font-size: 18px;
color: #202531;
font-weight: bold;
}
.process_desc img {
height: 25px;
width: 25px;
vertical-align: middle;
}
.process_end {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.process_end div {
text-align: center;
}
.step_icon {
color: #fff;
font-weight: 600;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.icon_box {
display: inline-block;
background-color: #2b4695;
width: 48px;
height: 48px;
border-radius: 99px;
position: relative;
border: 3px solid #b0bee8;
vertical-align: middle;
}
.circle {
display: inline-block;
background-color: #a9b1c7;
width: 24px;
height: 24px;
border-radius: 99px;
position: relative;
border: 4px solid #e6e9ef;
vertical-align: middle;
}
.content_process :deep() .el-step.is-horizontal .el-step__line {
height: 4px;
}
.content_process :deep() .el-step__head.is-finish .el-step__line {
background: linear-gradient(to right, #2b4695 50%, #e6e9ef 50%);
}
.process_complete :deep() .el-step__head.is-finish .el-step__line {
background: linear-gradient(to right, #2b4695 100%, #e6e9ef 0%);
}
</style>
<template>
<el-form
:label-position="'right'"
label-width="120px"
:model="systemForm"
:rules="formRules"
ref="systemRef"
style="max-width: 66%">
<el-form-item label="所属组织" prop="organization_id">
<el-tree-select
v-model="systemForm.organization_id"
:data="orgData"
:props="treeProps"
:render-after-expand="false"
:disabled="formType"
style="width: 100%" />
</el-form-item>
<el-form-item label="业务系统编码" prop="business_code" v-if="formType">
<el-input v-model="systemForm.business_code" :disabled="formType" />
</el-form-item>
<el-form-item label="业务系统名称" prop="business_name">
<el-input v-model="systemForm.business_name" />
</el-form-item>
<el-form-item label="AppId" prop="appid" v-if="formType">
<el-input v-model="systemForm.appid" :disabled="formType">
<template #suffix>
<bg-icon
icon="#bg-ic-copy"
style="cursor: pointer"
@click="copyText(systemForm.appid)"></bg-icon>
</template>
</el-input>
<!-- <span class="pl-1"><el-button type="primary" @click="copyText(systemForm.appid)">复制</el-button></span> -->
</el-form-item>
<el-form-item label="AppSecret" prop="appsecret" v-if="formType">
<div style="display: flex; width: 100%">
<el-input v-model="systemForm.appsecret" :disabled="formType">
<template #suffix>
<bg-icon
icon="#bg-ic-copy"
style="cursor: pointer"
@click="copyText(systemForm.appsecret)"></bg-icon>
</template>
</el-input>
<span class="pl-1"><el-button type="primary" @click="resetSecret">重置</el-button></span>
</div>
</el-form-item>
<el-form-item label="系统LOGO" prop="logo">
<bg-upload-image
v-model="systemForm.logo"
:showTips="true"
:limit="1"
:fileSize="500"
:fileSizeUnit="'KB'"
listType="picture-card"
:accept="['.jpg', '.jpeg', '.png']"
customTips="请选择图片上传:大小120 * 120像素支持jpg、png等格式,图片需小于500KB"></bg-upload-image>
</el-form-item>
<el-form-item label="业务系统概述" prop="business_desc">
<el-input
type="textarea"
:rows="3"
v-model="systemForm.business_desc"
show-word-limit
maxlength="300" />
</el-form-item>
<el-form-item label="角色" prop="system_role_id" v-if="!formType">
<el-select
v-model="systemForm.system_role_id"
placeholder="请选择角色"
:disabled="true"
style="width: 100%">
<el-option
v-for="item in roleList"
:key="item.role_id"
:label="item.role_name"
:value="item.role_id" />
</el-select>
</el-form-item>
<el-form-item label="账号" prop="system_account">
<el-input v-model="systemForm.system_account" />
</el-form-item>
<el-form-item label="手机号" prop="system_phone">
<el-input v-model="systemForm.system_phone" />
</el-form-item>
<el-form-item label="密码" prop="password" v-if="!formType">
<el-input :type="password_eye ? 'text' : 'password'" v-model="systemForm.password">
<template #suffix>
<bg-icon
@click="password_eye = !password_eye"
class="icon_eye"
icon="#bg-ic-eye"></bg-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="确认密码" prop="confirm_password" v-if="!formType">
<el-input :type="confirm_eye ? 'text' : 'password'" v-model="systemForm.confirm_password">
<template #suffix>
<bg-icon @click="confirm_eye = !confirm_eye" class="icon_eye" icon="#bg-ic-eye"></bg-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="访问地址" prop="access_address">
<el-input v-model="systemForm.access_address" />
</el-form-item>
<el-form-item label="开发厂商名称" prop="develop_id">
<el-select v-model="systemForm.develop_id" placeholder="请选择开发厂商" style="width: 100%">
<el-option
v-for="item in firmList"
:key="item.dict_id"
:label="item.dict_name"
:value="item.dict_id" />
</el-select>
</el-form-item>
<el-form-item label="是否启用" prop="state">
<bg-switch :labels="['否', '是']" :values="[0, 1]" v-model="systemForm.state"></bg-switch>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, ref, onBeforeMount, onMounted, computed } from "vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { validatePhone } from "@/services/rules.js";
const props = defineProps({
formType: {
type: Boolean,
default: false, //false 新增 true 编辑
},
id: {
type: String,
default: "",
},
});
const systemForm = reactive({
organization_id: "",
business_code: "",
business_name: "",
business_desc: "",
logo: [],
system_role_id: "",
system_account: "",
system_phone: "",
password: "",
confirm_password: "",
access_address: "",
develop_id: "",
state: 1,
appid: "",
appsecret: "",
});
const password_eye = ref(false);
const confirm_eye = ref(false);
const validatePass = (rule, value, callback) => {
if (value !== systemForm.password) {
callback(new Error("密码输入不一致"));
} else {
callback();
}
};
const validateBusinessName = (rule, value, callback) => {
let reg = /^[a-zA-Z0-9\u4e00-\u9fa5]+$/;
if (!reg.test(value)) {
callback(new Error("只能输入字母、数字和汉字"));
} else {
let params = null;
if (props.id) {
params = {
id: parseInt(props.id),
business_name: value,
organization_id: systemForm.organization_id,
};
} else {
params = { business_name: value, organization_id: systemForm.organization_id };
}
axios.post(`/apaas/system/v5/user/check/business`, params).then((res) => {
if (res.data.code == 200) {
callback();
} else {
callback(new Error(res.data.data));
}
});
}
};
const validateSystemAccount = (rule, value, callback) => {
let reg = /^[a-zA-Z0-9]+$/;
if (!reg.test(value)) {
callback(new Error("只能输入字母和数字"));
} else {
let params = null;
if (props.id) {
params = { id: parseInt(props.id), system_account: value };
} else {
params = { system_account: value };
}
axios.post(`/apaas/system/v5/user/check/account`, params).then((res) => {
if (res.data.code == 200) {
callback();
} else {
callback(new Error(res.data.data));
}
});
}
};
const checkPhoneRepet = (rule, value, callback) => {
let params = null;
if (props.id) {
params = { id: parseInt(props.id), contact_phone: value };
} else {
params = { id: 0, contact_phone: value };
}
axios.post(`/apaas/system/v5/org/check`, params).then((res) => {
if (res.data.code == 200) {
callback();
} else {
callback(new Error(res.data.data));
}
});
};
const formRules = reactive({
organization_id: [{ required: true, message: "请选择组织", trigger: "change" }],
business_name: [
{ required: true, message: "请输入业务系统名称", trigger: "blur" },
{ max: 50, message: "业务系统名称最大长度为50个字符", trigger: "blur" },
{ validator: validateBusinessName, trigger: "blur" },
],
system_role_id: [{ required: true, message: "请选择角色", trigger: "blur" }],
system_account: [
{ required: true, message: "请输入账号", trigger: "blur" },
{ min: 4, message: "帐号长度不得低于4个字符", trigger: "blur" },
{ max: 20, message: "帐号最大长度为20个字符", trigger: "blur" },
{ validator: validateSystemAccount, trigger: "blur" },
],
system_phone: [
{ required: true, message: "请输入账号", trigger: "blur" },
{ validator: validatePhone, trigger: "blur" },
{ validator: checkPhoneRepet, trigger: "blur" },
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 8, message: "密码长度不得低于8位", trigger: "blur" },
],
confirm_password: [
{ required: true, message: "请确认密码", trigger: "blur" },
{ validator: validatePass, trigger: "blur" },
],
access_address: [{ max: 500, message: "访问地址最大长度为500个字符", trigger: "blur" }],
develop_id: [{ required: true, message: "请选择开发厂商", trigger: "change" }],
});
const systemRef = ref(null);
const emit = defineEmits(["action"]);
const roleList = ref([]);
const firmList = ref([]);
const submitForm = async () => {
if (!systemRef) return;
await systemRef.value.validate((valid, fields) => {
if (valid) {
emit("action", systemForm);
} else {
emit("action", null);
}
});
};
const clearForm = () => {
if (!systemRef) return;
systemRef.value.resetFields();
systemForm.system_role_id = roleList.value[0].role_id;
};
const setForm = (data) => {
Object.assign(systemForm, data);
};
const orgData = ref([]);
const treeProps = {
label: "name",
children: "Child",
value: "organization_id",
};
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 = orgDataTemp;
} else {
ElMessage.error(res.data.data);
}
});
};
const copyText = (data) => {
navigator.clipboard.writeText(data).then(
function () {
ElMessage.success("复制成功");
},
function () {}
);
};
const resetSecret = () => {
axios.get(`/apaas/system/v5/user/reset/secret/${props.id}`).then((res) => {
if (res.data.code == 200) {
systemForm.appsecret = res.data.data;
ElMessage.success("重置成功!");
} else {
ElMessage.error(res.data.data);
}
});
};
const getRoleList = () => {
axios.get(`/apaas/system/v5/org/select/role?is_admin=1`).then((res) => {
if (res.data.code == 200) {
roleList.value = res.data.data;
systemForm.system_role_id = roleList.value[0].role_id;
} else {
}
});
};
const getFirmList = () => {
axios.get(`/apaas/system/v5/dictionary/developer/list`).then((res) => {
if (res.data.code == 200) {
firmList.value = res.data.data || [];
} else {
ElMessage.error(res.data.data);
}
});
};
onBeforeMount(() => {
getOrgTree();
getRoleList();
getFirmList();
});
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="img_box">
<img v-if="logo" :src="logo" style="height: 96px; width: 96px; border-radius: 99px" alt="" />
<img
v-else
src="/src/assets/imgs/home_ic_user.png"
style="height: 96px; width: 96px; border-radius: 99px"
alt="" />
</div>
<div class="info_box">
<bg-info :data="baseInfo"></bg-info>
</div>
</div>
</div>
<div>
<div class="title">
<div><span class="icon_box"></span> 联系人信息</div>
<div class="dashed_line"></div>
</div>
<div class="info_content">
<div class="img_box"></div>
<div class="info_box">
<bg-info :data="contactInfo"></bg-info>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount } from "vue";
import { useRoute, useRouter } from "vue-router";
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: "AppID",
value: "",
nameWidth: 130,
},
{
name: "AppSecret",
value: "",
copy_icon: true,
secret: true,
nameWidth: 130,
},
{
name: "创建人",
value: "",
nameWidth: 130,
},
{
name: "创建时间",
value: "",
nameWidth: 130,
},
{
name: "是否启用",
value: "",
state: "",
nameWidth: 130,
},
{
name: "开发厂商名称",
value: "",
nameWidth: 130,
},
{
name: "访问地址",
value: "",
full: true,
nameWidth: 130,
},
{
name: "业务系统描述",
value: "",
full: true,
nameWidth: 130,
},
]);
const contactInfo = reactive([
{
name: "联系人姓名",
value: "",
nameWidth: 130,
},
{
name: "联系人手机号",
value: "",
nameWidth: 130,
},
{
name: "联系人邮箱",
value: "",
full: true,
nameWidth: 130,
},
{
name: "备注",
value: "",
full: true,
nameWidth: 130,
},
]);
const logo = ref("");
const getDetail = () => {
axios.get(`/apaas/system/v5/user/detail/${route.query.id}`).then((res) => {
if (res.data.code == 200) {
const detail = res.data.data;
baseInfo[0].value = detail.organization_name;
baseInfo[1].value = detail.business_code;
baseInfo[2].value = detail.business_name;
baseInfo[3].value = detail.system_account;
baseInfo[4].value = detail.app_id;
baseInfo[5].value = detail.app_secret;
baseInfo[6].value =
detail.created_by + "(" + (detail.created_user_role ? detail.created_user_role.join("") : "") + ")";
baseInfo[7].value = detail.created_time;
baseInfo[8].value = detail.state === 1 ? "" : "";
baseInfo[8].state = detail.state === 1 ? "success" : "default";
baseInfo[9].value = detail.develop_name;
baseInfo[10].value = detail.access_address;
baseInfo[11].value = detail.business_desc;
contactInfo[0].value = detail.contact_name;
contactInfo[1].value = detail.contact_phone;
contactInfo[2].value = detail.contact_email;
contactInfo[3].value = detail.remark;
logo.value = detail.logo;
} else {
ElMessage.error(res.data.data);
}
});
};
onBeforeMount(() => {
getDetail();
});
</script>
<style scoped>
.page_content {
padding: 15px;
overflow: auto;
}
.info_content {
display: flex;
}
.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;
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="flex_row">
<div class="flex_left bgc_white">
<div class="tree_header">政务组织</div>
<div class="tree_content">
<div class="search">
<el-input v-model="filterTree" placeholder="请输入组织名称搜索" :prefix-icon="Search" />
</div>
<div class="tree">
<el-tree
ref="treeRef"
:data="orgData"
:props="defaultProps"
@node-click="handleNodeClick"
node-key="id"
:highlight-current="true"
:filter-node-method="filterNode"
:default-expand-all="true">
<template #default="{ node, data }">
<div class="custom_tree_node">
<span class="label-text" :title="node.label">{{ node.label }} </span>
</div>
</template>
</el-tree>
</div>
</div>
</div>
<div class="flex_right bgc_white">
<div class="main_container">
<bg-filter-group @search="changeSearch" v-model="filter.search" placeholder="请输入关键字">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="addAccount">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
<el-button @click="deleteBatch"> 批量删除 </el-button>
<el-button @click="resetPsd"> 重置密码 </el-button>
<span class="header_info"
>已选择<span style="color: #202531; font-weight: bold">{{ selected.length }}</span
></span
>
<span class="header_info can_click_text" @click="clearSelected">清空</span>
</div>
</template>
</bg-filter-group>
<div class="table_container apaas_scroll">
<bg-table
ref="dataTable"
:headers="headers"
:rows="tableRows"
:isIndex="true"
:stripe="true"
:select="true"
@selectAc="selectRows">
<template v-slot:business_name="{ row }">
<span @click="goDetail(row)" class="can_click_text">
{{ row.business_name }}
</span>
</template>
<template v-slot:state="{ row }">
<bg-switch
@click="stateChange(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.state"></bg-switch>
</template>
<template v-slot:action="{ row }">
<bg-table-btn @click="editAccount(row.id)"> 编辑 </bg-table-btn>
<bg-table-btn @click="editPsdAccount(row)"> 修改密码 </bg-table-btn>
<bg-table-btn @click="deleteAccount(row)"> 删除 </bg-table-btn>
</template>
</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>
<el-dialog v-model="deleteFlag" title="删除" width="520px" :before-close="handleCloseDelete">
<div class="warning_info">
<bg-icon
style="font-size: 12px; color: #a9b1c7; margin-right: 8px; vertical-align: baseline"
icon="#bg-ic-circle-tips"></bg-icon>
您正在对 <span class="danger_info">{{ selectedName.join("、") }}</span
>做删除操作。
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseDelete">取消</el-button>
<el-button type="primary" @click="deleteConfirm">确定</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="deleteRowFlag" title="删除" width="520px" :before-close="handleCloseRowDelete">
<div class="warning_info">
<bg-icon
style="font-size: 12px; color: #a9b1c7; margin-right: 8px; vertical-align: baseline"
icon="#bg-ic-circle-tips"></bg-icon>
您正在对 <span class="danger_info">{{ selectedRow.business_name }}</span
>做删除操作。
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseRowDelete">取消</el-button>
<el-button type="primary" @click="deleteRowConfirm">确定</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="resetFlag" title="重置密码" width="520px" :before-close="handleCloseReset">
<div class="warning_info">
<bg-icon
style="font-size: 12px; color: #a9b1c7; margin-right: 8px; vertical-align: baseline"
icon="#bg-ic-circle-tips"></bg-icon>
您正在对 <span class="danger_info">{{ selectedName.join("、") }}</span
>做重置密码操作。
</div>
<div>重置密码为123456,是否继续?</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseReset">取消</el-button>
<el-button type="primary" @click="resetConfirm">确定</el-button>
</span>
</template>
</el-dialog>
<el-dialog v-model="editFlag" title="修改密码" width="520px" :before-close="handleCloseEdit">
<div class="warning_info">
<bg-icon
style="font-size: 12px; color: #a9b1c7; margin-right: 8px; vertical-align: baseline"
icon="#bg-ic-circle-tips"></bg-icon>
您正在对 <span class="danger_info">{{ selectedRow.business_name }}</span
>做修改密码操作,修改后旧密码将无法登录。
</div>
<div>
<el-form
:label-position="'right'"
label-width="120px"
:model="passwordForm"
:rules="passwordFormRules"
ref="passwordRef"
style="max-width: 80%">
<el-form-item label="新密码" prop="password">
<el-input
v-model="passwordForm.password"
:type="password_eye ? 'text' : 'password'"
placeholder="请输入密码">
<template #suffix>
<bg-icon @click="password_eye = !password_eye" class="icon_eye" icon="#bg-ic-eye"></bg-icon>
</template>
</el-input>
</el-form-item>
<el-form-item label="确认新密码" prop="confirm_password">
<el-input
v-model="passwordForm.confirm_password"
:type="confirm_eye ? 'text' : 'password'"
placeholder="请确认新密码">
<template #suffix>
<bg-icon @click="confirm_eye = !confirm_eye" class="icon_eye" icon="#bg-ic-eye"></bg-icon>
</template>
</el-input>
</el-form-item>
</el-form>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseEdit">取消</el-button>
<el-button type="primary" @click="editConfirm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { watch, nextTick, reactive, toRefs, ref, computed, onBeforeMount } from "vue";
import { useRoute, useRouter } from "vue-router";
import { edit } from "ace-builds";
import CryptoJS from "crypto-js";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const dataTable = ref(null);
const headers = ref([
{
label: "业务系统编码",
prop: "business_code",
minWidth: 160,
},
{
label: "业务系统名称",
prop: "business_name",
minWidth: 160,
},
{
label: "账号",
prop: "system_account",
},
{
label: "所属组织",
prop: "organization_name",
minWidth: 180,
},
{
label: "创建时间",
prop: "created_time",
width: 160,
},
{
label: "是否启用",
prop: "state",
},
{
label: "操作",
prop: "action",
width: 220,
fixed: "right",
},
]);
const tableRows = ref([]);
const search = ref("");
const filter = reactive({
search: "",
page: 1,
limit: 10,
});
const tableTotal = ref(0);
const deleteFlag = ref(false);
const deleteRowFlag = ref(false);
const resetFlag = ref(false);
const selectedRow = ref({});
const selected = ref([]);
const selectedName = ref([]);
const editFlag = ref(false);
const password_eye = ref(false);
const confirm_eye = ref(false);
const passwordRef = ref(null);
const passwordForm = reactive({
password: "",
confirm_password: "",
});
const selectNode = ref(null);
const selectNodeObj = ref(null);
const filterTree = ref("");
const treeRef = ref(null);
const validatePass = (rule, value, callback) => {
if (value !== passwordForm.password) {
callback(new Error("密码输入不一致"));
} else {
callback();
}
};
const passwordFormRules = reactive({
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
{ min: 8, message: "密码长度不得低于8位", trigger: "blur" },
],
confirm_password: [
{ required: true, message: "请确认密码", trigger: "blur" },
{ validator: validatePass, trigger: "blur" },
],
});
const router = useRouter();
const route = useRoute();
const getTableRows = () => {
let params = { ...filter, organization_id: selectNode.value };
axios
.get(`/apaas/system/v5/user/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
tableRows.value = res.data.data || [];
tableTotal.value = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
const addAccount = (params) => {
router.push({
path: "/develop/account/add",
});
};
const handleNodeClick = (data) => {
if (data.data_type == 1) {
selectNode.value = data.id;
changePage(1);
} else {
nextTick(() => {
treeRef.value.setCurrentKey(null);
});
}
};
const orgData = ref([]);
const customNodeClass = (data, node) => {
if (data.data_type == 0) {
return "noFocus";
} else {
return "";
}
};
const defaultProps = {
label: "name",
children: "Child",
value: "organization_id",
class: customNodeClass,
};
const getOrgTree = () => {
axios.get(`/apaas/system/v5/org/tree`).then((res) => {
if (res.data.code == 200) {
orgData.value = res.data.data || [];
orgData.value.shift();
const orgList = searchOrg(orgData.value);
if (route.query.id) {
searchItem(orgData.value, route.query.id);
} else {
selectNode.value = orgList.length > 0 ? orgList[0].id : "";
}
nextTick(() => {
if (route.query.id) {
treeRef.value.setCurrentNode(selectNodeObj.value);
} else {
if (orgList.length > 0) {
treeRef.value.setCurrentNode(orgList[0]);
}
}
});
getTableRows();
} else {
ElMessage.error(res.data.data);
}
});
};
const searchItem = (data, id) => {
data.forEach((e) => {
if (e.organization_id == id) {
selectNodeObj.value = e;
selectNode.value = e.id;
} else {
if (e.Child) {
searchItem(e.Child, id);
}
}
});
};
const searchOrg = (data) => {
const arr = [];
data.forEach((item) => {
if (item.data_type === 1) {
arr.push(item);
return;
} else {
if (item.Child) {
searchOrg(item.Child);
} else {
return;
}
}
});
return arr;
};
watch(filterTree, (val) => {
treeRef.value.filter(val);
});
const filterNode = (value, data) => {
if (!value) return true;
return data.name.includes(value);
};
const stateChange = (row) => {
const state = row.state.toString();
axios.post(`/apaas/system/v5/user/state/${row.id}/${state}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getTableRows();
} else {
ElMessage.error(res.data.data);
row.state = row.state == 0 ? 1 : 0;
}
});
};
const changeSize = (size) => {
filter.limit = size;
filter.page = 1;
changePage(1);
};
const changePage = (page) => {
filter.page = page;
getTableRows();
};
const changeSearch = (val) => {
filter.search = val;
changePage(1);
}; // 表格关键字筛选
const goDetail = (params) => {
router.push({
path: "/develop/account/detail",
query: {
id: params.id,
},
});
};
const editAccount = (id) => {
router.push({
path: "/develop/account/edit",
query: {
id: id,
},
});
};
const deleteAccount = (data) => {
if (data.state === 0) {
selectedRow.value = data;
deleteRowFlag.value = true;
} else {
ElMessage.error("当前状态已启用,不可删除!");
}
};
const handleCloseDelete = () => {
deleteFlag.value = false;
};
const handleCloseRowDelete = () => {
deleteRowFlag.value = false;
};
const deleteConfirm = () => {
const ids = selected.value.map((item) => item.id);
axios.delete(`/apaas/system/v5/user/delete`, { data: { ids: ids } }).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getTableRows();
} else {
ElMessage.error(res.data.data);
}
handleCloseDelete();
});
};
const deleteRowConfirm = () => {
axios.delete(`/apaas/system/v5/user/delete`, { data: { ids: [selectedRow.value.id] } }).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getTableRows();
} else {
ElMessage.error(res.data.data);
}
handleCloseRowDelete();
});
};
const deleteBatch = () => {
if (selectedName.value.length > 0) {
deleteFlag.value = true;
} else {
ElMessage.error("请先选择需要操作的数据!");
}
};
const handleCloseReset = () => {
resetFlag.value = false;
};
const resetPsd = () => {
if (selectedName.value.length > 0) {
resetFlag.value = true;
} else {
ElMessage.error("请先选择需要操作的数据!");
}
};
const resetConfirm = () => {
const ids = selected.value.map((item) => item.id);
axios.post(`/apaas/system/v5/user/resetpwd`, { ids: ids }).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getTableRows();
} else {
ElMessage.error(res.data.data);
}
handleCloseReset();
});
};
const editPsdAccount = (data) => {
selectedRow.value = data;
editFlag.value = true;
};
const handleCloseEdit = () => {
if (!passwordRef) return;
passwordRef.value.resetFields();
editFlag.value = false;
password_eye.value = false;
confirm_eye.value = false;
};
const editConfirm = () => {
if (!passwordRef) return;
passwordRef.value.validate((valid, fields) => {
if (valid) {
axios
.post(`/apaas/system/v5/user/editpwd`, {
id: selectedRow.value.id,
password: CryptoJS.AES.encrypt(passwordForm.password, "swuE9cmCZQwrkYRV").toString(),
})
.then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
handleCloseEdit();
});
}
});
};
const selectRows = (data) => {
selected.value = data.selection;
selectedName.value = data.selection.map((item) => item.business_name);
};
const clearSelected = () => {
dataTable.value.clearTable();
};
onBeforeMount(() => {
getOrgTree();
//getTableRows()
});
</script>
<style scoped>
.tree_header {
height: 40px;
line-height: 40px;
background-color: #f7f7f9;
border-radius: 6px 6px 0px 0px;
font-size: 16px;
letter-spacing: 0px;
color: #202531;
padding-left: 16px;
}
.tree_content {
overflow: hidden;
height: calc(100% - 45px);
}
.search {
padding: 16px;
}
.tree {
overflow: auto;
height: calc(100% - 65px);
}
.tree :deep() .el-tree-node > .el-tree-node__children {
overflow: unset;
}
.tree :deep().noFocus:focus > .el-tree-node__content {
background-color: #fff;
}
.custom_tree_node {
width: 100%;
font-size: 14px;
color: #202531;
padding-right: 16px;
}
.label-text:hover {
padding: 3px 0;
background-color: var(--el-tree-node-hover-bg-color);
}
.main_container {
height: 100%;
}
.table_container {
height: calc(100% - 30px);
overflow: auto;
padding: 0 16px;
}
.pagination_box {
position: sticky;
position: -webkit-sticky;
margin-top: 16px;
bottom: 25px;
background-color: #fff;
z-index: 1024;
height: 40px;
line-height: 40px;
padding-top: 5px;
}
.bg-pagination {
bottom: unset;
margin-top: 0px;
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="flex_row">
<div class="flex_left">
<div class="tree_content">
<div class="search">
<el-input v-model.trim="search" placeholder="请输入内容" :prefix-icon="Search" />
</div>
<div class="tree">
<el-tree
class="file-tree"
:data="data"
:props="defaultProps"
@node-click="handleNodeClick"
:default-expand-all="true"
:highlight-current="true"
node-key="menu_id"
ref="menuTree"
:filter-node-method="filterNode"
:expand-on-click-node="false">
<template #default="{ node, data }">
<div class="custom-tree-node" pointer-events="none">
<span class="label-text text_clip" :title="data.menu_name">{{ data.menu_name }}</span>
</div>
<span
class="tree-action-box"
:class="{ position_sticky: isSticky(data.name) }"
@click.stop="showAction($event, data, node)">
<bg-icon class="tree-more" icon="#bg-ic-s-more"></bg-icon>
</span>
</template>
</el-tree>
<Teleport to="body">
<div
class="tree-action"
ref="treeAction"
v-show="actionFlag"
:style="{ top: acTop, left: acLeft, bottom: acBottom }">
<div class="action" @click="fileAction(1, selectData, selectParentData)">新增本级</div>
<div class="action" @click="fileAction(2, selectData, selectParentData)">新增下级</div>
<div class="action" @click="fileAction(3, selectData, selectParentData)">删除</div>
<div
class="action"
:class="{ disable: moveIndex == 0 }"
@click="fileAction(4, selectData, selectParentData, moveIndex == 0)">
上移
</div>
<div
class="action"
:class="{
disable: !selectParentData
? moveIndex == data.length - 1
: moveIndex == selectParentData.Child.length - 1,
}"
@click="
fileAction(
5,
selectData,
selectParentData,
!selectParentData ? moveIndex == data.length - 1 : moveIndex == selectParentData.Child.length - 1
)
">
下移
</div>
</div>
</Teleport>
</div>
</div>
</div>
<div class="flex_right">
<div class="main_container">
<div class="base-info bg-scroll">
<menuForm ref="menuFormRef" :data="menuFormData"></menuForm>
</div>
<div class="base-btn">
<el-button type="primary" @click="saveMenu(menuFormRef, addType)"> 保存 </el-button>
</div>
</div>
</div>
</div>
<!-- 删除弹窗 -->
<el-dialog class="dialog_box" title="提示" v-model="delDialog" width="400px">
<div>确定要删除此菜单吗?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="delDialog = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { reactive, toRefs, ref, nextTick, computed, onBeforeMount, onBeforeUnmount, watch } from "vue";
import { useRouter } from "vue-router";
import { ElMessage } from "element-plus";
import axios from "../../../../request/http.js";
import menuForm from "./menu-form.vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const router = useRouter();
const menuTree = ref(null); // 树形
const treeAction = ref(null); // 树形操作弹出框
const menuFormRef = ref(null); // 右侧表单
const acTop = ref("");
const acLeft = ref("");
const acBottom = ref("");
const actionFlag = ref(false); // 弹出框状态标识
const state = reactive({
data: [], // 树形数据
search: "", // 树形搜索
selectData: null, // 当前选中的节点
selectParentData: null, // 当前选中节点的父节点
treeAction,
menuTree,
bottomGap: 30, //弹窗吸底高度
defaultProps: {
children: "Child",
label: "menu_name",
}, // 树形配置
actionDataIndex: 0, // 当前选中节点在本级的位置,用来判断是否可以上下移动
menuFormData: null, // 菜单数据
addType: 1, // 1-新增本级 2-新增下级
newMenuId: 0, // 新增菜单时用来指定menu_id,达到选中效果
timer: null, // 定时器
delDialog: false,
});
const moveIndex = computed({
get: () => {
let index = 0;
if (state.selectData) {
findIndex(state.selectData.menu_id, state.data);
index = state.actionDataIndex;
}
return index;
},
}); // 计算上下移动
watch(
() => state.search,
(n, o) => {
if (state.timer) {
clearTimeout(state.timer);
}
state.timer = setTimeout(() => {
state.menuTree.filter(n);
}, 500);
}
); // 树形搜索值
const getMenuTree = (type, id) => {
axios.get(`/apaas/system/v5/menu/tree`).then((res) => {
if (res.data.code == 200) {
state.data = res.data.data || [];
nextTick().then(() => {
if (type) {
if (type == 1) {
// 删除数据后默认选中父节点或第一个节点
state.menuTree && state.menuTree.setCurrentKey(state.selectParentData.menu_id);
handleNodeClick(state.selectParentData);
nextTick().then(() => {
setScroll();
});
} else {
state.menuTree && state.menuTree.setCurrentKey(state.data[0].menu_id);
handleNodeClick(state.data[0]);
}
} else {
// 上下移,初始化时默认选中节点 新增选中新增的节点
if (id) {
findData(state.data, id);
}
if (state.selectData) {
state.menuTree && state.menuTree.setCurrentKey(state.selectData.menu_id);
handleNodeClick(state.selectData);
nextTick().then(() => {
setScroll();
});
} else {
state.menuTree && state.menuTree.setCurrentKey(state.data[0].menu_id);
handleNodeClick(state.data[0]);
}
}
});
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取菜单树
const setScroll = () => {
setTimeout(() => {
var nodeOffsetTop = document.querySelector(".file-tree .is-current").offsetTop;
var treeScroll = document.querySelector(".tree");
var parentHeight = treeScroll.clientHeight;
if (nodeOffsetTop > parentHeight) {
treeScroll.scrollTop = nodeOffsetTop - parentHeight / 2;
}
}, 300);
};
const findData = (arr, id) => {
arr.forEach((e) => {
if (e.menu_id == id) {
state.selectData = e;
} else {
if (e.Child && e.Child.length > 0) {
findData(e.Child, id);
}
}
});
}; // 处理数据 用于新增菜单后选中新增的菜单
const findIndex = (code, arr) => {
arr.forEach((e, i) => {
if (e.menu_id == code) {
state.actionDataIndex = i;
} else {
if (e.Child && e.Child.length > 0) {
findIndex(code, e.Child);
}
}
});
}; // 处理数据 用于判断能否上下移
const isSticky = (data) => {
const canvas = document.createElement("canvas");
const context = canvas.getContext("2d");
context.font = "14px MicrosoftYaHei";
const { width } = context.measureText(data);
return width > 255 ? true : false;
};
const showAction = (e, data, node) => {
acTop.value = "";
acLeft.value = "";
acBottom.value = "";
const rect = e.target.getBoundingClientRect(); //获取点击的dom的位置
var allHeight = document.body.scrollHeight;
actionFlag.value = true;
state.selectData = data;
if (node.parent.data.Child) {
state.selectParentData = node.parent.data;
} else {
//没有children则是最上层元素
state.selectParentData = null;
}
nextTick().then(() => {
setTimeout(() => {
var height = window.getComputedStyle(state.treeAction).height;
height = parseInt(height);
//判断弹窗位置是否超过屏幕,超过则吸底展示
if (height + rect.y - 17 > allHeight - state.bottomGap) {
acBottom.value = state.bottomGap + "px";
acLeft.value = rect.x + 35 + "px";
} else {
acTop.value = rect.y - 17 + "px";
acLeft.value = rect.x + 35 + "px";
}
});
});
}; // 树形操作框
const handleNodeClick = (data) => {
if (state.selectData && state.selectData.menu_id == data.menu_id) {
return;
}
state.selectData = data;
menuFormRef.value.setForm(state.selectData);
closeAction();
if (menuFormRef) {
nextTick().then(() => {
menuFormRef.value.menuRef.validate((valid) => {
if (!valid) {
menuFormRef.value.menuRef.clearValidate();
}
});
});
} // 清除表单验证
}; // 菜单树节点点击
const filterNode = (val, data) => {
if (!val) {
return true;
}
return data.menu_name.includes(val);
}; // 树形筛选
const fileAction = (val, data, parent, disabled = false) => {
if (val == 1) {
// 新增本级菜单
state.addType = 1;
let newChild = {
menu_name: "新菜单", // 菜单名称
menu_id: "new" + state.newMenuId, // 菜单id,用于新增后选中
menu_type: !parent ? 0 : parent.menu_type == 0 ? 0 : 2, // 新增本级的类型
dict_group_id: parent ? parent.dict_group_id : "", // 父级的分组 有则继承,无则自选
state: 1, // 启用状态
menu_url: "", // 路径
menu_logo: "", // 图标
source: "",
level: data.level, // 当前的level
p_menu_type: parent ? parent.menu_type : 0, // 父级的类型,用于判断菜单下不能建目录
system_type: "", // 系统类型
new_window: 0,
remark: "",
};
state.menuTree.insertAfter(
newChild,
parent ? parent.Child[parent.Child.length - 1] : state.data[state.data.length - 1]
);
state.menuTree && state.menuTree.setCurrentKey(newChild.menu_id);
handleNodeClick(newChild);
state.newMenuId++;
} else if (val == 2) {
// 新增下级菜单
if (disabled) {
return;
}
state.addType = 2;
let newChild = {
menu_name: "新菜单",
menu_id: "new" + state.newMenuId,
menu_type: data.menu_type == 0 ? 0 : 2,
dict_group_id: data.dict_group_id,
state: 1,
menu_url: "",
menu_logo: "",
source: "",
p_menu_type: data.menu_type,
level: data.level + 1,
system_type: "", // 系统类型
new_window: 0,
remark: "",
};
state.menuTree.append(newChild, data);
state.menuTree && state.menuTree.setCurrentKey(newChild.menu_id);
handleNodeClick(newChild);
state.selectParentData = data;
state.newMenuId++;
} else if (val == 3) {
// 删除菜单
if (data.id) {
if (data.state == 1) {
ElMessage.error("删除内容中有启用状态的数据,不可删除!");
return;
}
state.delDialog = true;
} else {
state.delDialog = true;
}
} else if (val == 4) {
// 菜单上移
if (disabled) {
return;
}
let nextObj = null;
if (parent) {
parent.Child.forEach((e, i) => {
if (data.id == e.id) {
nextObj = parent.Child[i - 1];
}
});
} else {
state.data.forEach((e, i) => {
if (data.id == e.id) {
nextObj = state.data[i - 1];
}
});
}
changeSort(data, nextObj);
} else {
// 菜单下移
if (disabled) {
return;
}
let nextObj = null;
if (parent) {
parent.Child.forEach((e, i) => {
if (data.id == e.id) {
nextObj = parent.Child[i + 1];
}
});
} else {
state.data.forEach((e, i) => {
if (data.id == e.id) {
nextObj = state.data[i + 1];
}
});
}
changeSort(data, nextObj);
}
if (menuFormRef) {
nextTick().then(() => {
menuFormRef.value.menuRef.validate((valid) => {
if (!valid) {
menuFormRef.value.menuRef.clearValidate();
}
});
});
} // 清除表单验证
}; // 各种操作按钮
const deleteData = () => {
if (state.selectData.id) {
axios.delete(`/apaas/system/v5/menu/${state.selectData.id}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.selectData = null;
let type = state.selectParentData ? 1 : 2; // 1--有父级 2--无父级
getMenuTree(type);
state.delDialog = false;
} else {
ElMessage.error(res.data.data);
}
});
} else {
state.menuTree.remove(state.selectData);
state.selectData = null;
let type = state.selectParentData ? 1 : 2; // 1--有父级 2--无父级
if (type == 1) {
state.menuTree && state.menuTree.setCurrentKey(state.selectParentData.menu_id);
handleNodeClick(state.selectParentData);
} else {
state.menuTree && state.menuTree.setCurrentKey(state.data[0].menu_id);
handleNodeClick(state.data[0]);
}
state.delDialog = false;
}
};
const changeSort = (data, nextObj) => {
let params = [
{
id: data.id,
sort: nextObj.sort,
},
{
id: nextObj.id,
sort: data.sort,
},
];
axios.put(`/apaas/system/v5/menu/sort`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getMenuTree();
} else {
ElMessage.error(res.data.data);
}
});
}; // 菜单上下移
const closeAction = () => {
actionFlag.value = false;
}; // 点击其他地方,菜单操作框关闭
const saveMenu = (el, type) => {
el.menuRef.validate((valid) => {
if (valid) {
let params = {
menu_name: el.menuForm.menu_name,
menu_type: el.menuForm.menu_type,
dict_group_id: el.menuForm.dict_group_id,
state: el.menuForm.state,
menu_url: el.menuForm.menu_url,
menu_logo: el.menuForm.menu_logo,
source: el.menuForm.menu_type == 0 ? "" : el.menuForm.source,
system_type: el.menuForm.system_type,
new_window: el.menuForm.new_window,
remark: el.menuForm.remark,
};
if (state.selectData.id) {
// 编辑
axios.put(`/apaas/system/v5/menu/${state.selectData.id}`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getMenuTree();
} else {
ElMessage.error(res.data.data);
}
});
} else {
// 新增
if (state.selectParentData) {
if (state.selectParentData.menu_type == 1 && el.menuForm.menu_type == 0) {
ElMessage.error("菜单之下不可以新增目录");
return;
}
}
if (type == 1) {
// 新增本级
params.p_menu_id = state.selectParentData ? state.selectParentData.menu_id : "";
} else {
// 新增下级
params.p_menu_id = state.selectParentData.menu_id;
}
axios.post(`/apaas/system/v5/menu/add`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.selectData = null;
getMenuTree(null, res.data.data);
} else {
ElMessage.error(res.data.data);
}
});
}
}
});
}; // 保存按钮,新增或编辑菜单
onBeforeMount(() => {
window.addEventListener("click", closeAction);
window.addEventListener("scroll", closeAction, true);
getMenuTree();
});
onBeforeUnmount(() => {
window.removeEventListener("click", closeAction);
window.removeEventListener("scroll", closeAction);
});
const { data, search, defaultProps, selectParentData, selectData, menuFormData, addType, delDialog } = toRefs(state);
</script>
<style scoped>
.flex_left {
background-color: #fff;
height: calc(100% - 16px);
width: 320px;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin-right: 10px;
overflow: hidden;
}
.flex_right {
background-color: #fff;
height: calc(100% - 16px);
flex: 1;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
overflow: hidden;
}
.tree_content {
overflow: hidden;
height: calc(100% - 4px);
position: relative;
}
.search {
padding: 16px 16px 10px;
}
.tree {
overflow: auto;
height: calc(100% - 65px);
}
.tree :deep().el-tree-node > .el-tree-node__content {
/* padding: 0 16px!important; */
}
.tree :deep() .el-tree-node > .el-tree-node__children {
overflow: unset;
/* padding-left: 16px; */
}
.file-tree :deep() .el-tree-node__content {
position: relative;
}
.tree-action-box {
display: none;
position: absolute;
right: 0px;
top: 0px;
background-color: #f2f3f7;
width: 44px;
text-align: center;
height: 36px;
line-height: 36px;
}
.position_sticky {
position: sticky;
position: -webkit-sticky;
}
.tree .el-tree-node__content:hover .tree-action-box {
display: inline-block;
}
.tree .el-tree {
position: static !important;
}
.tree .el-tree :deep().el-tree-node__content {
height: 36px !important;
}
.tree .el-tree :deep().el-tree-node__content:hover {
background-image: linear-gradient(90deg, rgba(255, 255, 255, 0.05) 83%, rgba(0, 0, 0, 0.05) 86%);
}
.tree-more {
font-size: 12px;
color: #3759be;
}
.tree-action {
width: 144px;
background-color: #ffffff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
padding: 4px 0;
border-radius: 4px;
position: fixed;
z-index: 9;
}
.tree-action .action {
width: 100%;
height: 34px;
line-height: 34px;
padding-left: 16px;
font-size: 14px;
color: #202531;
cursor: pointer;
}
.tree-action .action:hover {
background-color: #f2f3f7;
color: #3759be;
}
.tree-action .disable {
cursor: not-allowed;
color: #616f94;
}
.tree-action .disable:hover {
background-color: #fff;
color: #616f94;
}
.main_container {
height: 100%;
}
</style>
<style lang="scss" scoped>
.main_container {
.base-info {
height: calc(100% - 68px);
padding: 40px 0 0 40px;
}
.base-btn {
height: 68px;
border-top: 1px solid #e6e9ef;
text-align: right;
padding: 16px;
.el-button {
width: 92px;
}
}
}
</style>
<template>
<el-form
:label-position="'right'"
label-width="68px"
:model="menuForm"
:rules="formRules"
ref="menuRef"
style="width: 856px"
class="menu_form">
<el-form-item label="菜单名称" prop="menu_name">
<el-input v-model.trim="menuForm.menu_name" show-word-limit maxlength="8" placeholder="请输入菜单名称" />
</el-form-item>
<el-form-item label="类型" prop="menu_type" v-if="!topLevel">
<el-radio-group v-model="menuForm.menu_type" :disabled="radioDisabled">
<el-radio :label="0" :disabled="typeDisFlag != 0">目录</el-radio>
<el-radio :label="1" :disabled="typeDisFlag != 2 && typeDisFlag != 0">菜单</el-radio>
<el-radio :label="2">子页面</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="分组" prop="dict_group_id" v-if="!topLevel">
<el-select v-model="menuForm.dict_group_id" :disabled="groupFlag" placeholder="请选择分组">
<el-option v-for="item in groupList" :key="item.dict_id" :label="item.dict_name" :value="item.dict_id" />
</el-select>
</el-form-item>
<el-form-item label="跳转方式" prop="new_window" v-if="menuForm.menu_type != 0">
<el-radio-group v-model="menuForm.new_window">
<el-radio :label="0">当前窗口</el-radio>
<el-radio :label="1">新窗口</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="menuForm.remark" show-word-limit maxlength="8" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="系统类型" prop="system_type" v-if="topLevel">
<el-select v-model="menuForm.system_type" placeholder="请选择系统类型">
<el-option v-for="item in systemGroupList" :key="item.dict_id" :label="item.dict_name" :value="item.dict_id" />
</el-select>
</el-form-item>
<el-form-item label="是否启用" prop="state">
<el-switch
class="bg-switch-ele"
v-model="menuForm.state"
:active-value="1"
:inactive-value="0"
inline-prompt
active-text="是"
inactive-text="否"
:disabled="disabledSwitch" />
</el-form-item>
<el-form-item label="菜单路径" :prop="topLevel ? '' : 'menu_url'">
<el-input v-model="menuForm.menu_url" show-word-limit maxlength="200" placeholder="请输入菜单路径" />
</el-form-item>
<el-form-item label="菜单图标" prop="menu_logo">
<div class="iconBox" :style="iconFlag ? { 'border-color': '#2b4695' } : {}" @click="openIconbox">
<div>
<span>
<bg-icon class="bgIcon" style="margin-right: 8px" :icon="'#' + menuForm.menu_logo"></bg-icon>
<span>{{ menuForm.menu_logo.replace("bg-ic-", "") }}</span>
</span>
</div>
<div>
<bg-icon
class="bgIcon clearIcon"
icon="#bg-ic-circle-close"
v-if="menuForm.menu_logo != ''"
@click.stop="clearIcon"></bg-icon>
<bg-icon class="bgIcon" :icon="iconFlag ? '#bg-ic-caret-top' : '#bg-ic-caret-bottom'"></bg-icon>
</div>
</div>
<div class="icon_box" v-show="iconFlag">
<div class="input_box">
<el-input placeholder="请输入内容" :prefix-icon="Search" v-model="icon_search" @input="searchIcon">
</el-input>
</div>
<div class="icon_content bg-scroll">
<div class="icon_item" v-for="(e, i) in showIconArr" :key="i + 100" @click="changeIcon(e)">
<span :title="e.name">
<bg-icon class="selectBgIcon" :icon="'#' + e.icon"></bg-icon>
{{ e.name }}
</span>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="资源路径" v-if="menuForm.menu_type != 0">
<el-input v-model="menuForm.source" show-word-limit maxlength="200" placeholder="请输入资源路径" />
</el-form-item>
</el-form>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { reactive, ref, onMounted, toRefs } from "vue";
import axios from "../../../../request/http.js";
import { ElMessage } from "element-plus";
import allIconArr from "@/assets/js/bg_icon.js";
const menuRef = ref(null);
const props = defineProps({
data: {
type: Object,
default: null,
},
});
const state = reactive({
menuRef,
menuForm: {
menu_name: "",
menu_type: 0,
dict_group_id: "",
state: 1,
menu_url: "",
menu_logo: "",
source: "",
system_type: "",
new_window: 0,
remark: "",
},
formRules: {
menu_name: [{ required: true, message: "请输入菜单名称", trigger: "blur" }],
menu_type: [{ required: true, message: "请选择菜单类型", trigger: "change" }],
dict_group_id: [{ required: true, message: "请选择菜单分组", trigger: "change" }],
system_type: [{ required: true, message: "请选择系统类型", trigger: "change" }],
menu_url: [{ required: true, message: "请输入菜单路径", trigger: "blur" }],
new_window: [{ required: true, message: "请选择跳转方式", trigger: "change" }],
},
groupList: [], // 菜单分组
systemGroupList: [], // 系统类型--顶级菜单使用
iconFlag: false, // 图标盒子
icon_search: "", // 图标筛选
iconArr: [], // 所有的图标
showIconArr: [], // 展示的图标
timer: null,
radioDisabled: false,
topLevel: false,
typeDisFlag: null,
groupFlag: true,
disabledSwitch: false, // 内置菜单built_in为1-不可禁用 0或不存在-可禁用
});
const getGroupList = () => {
axios
.get(`/apaas/system/v5/menu/group/dict`)
.then((res) => {
if (res.data.code == 200) {
state.groupList = res.data.data || [];
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
}; // 获取分组数据
const getSystemGroup = () => {
axios
.get(`/apaas/system/v5/menu/system/dict`)
.then((res) => {
if (res.data.code == 200) {
state.systemGroupList = res.data.data || [];
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
}; // 获取系统类型数据
const openIconbox = () => {
state.iconFlag = !state.iconFlag;
state.icon_search = "";
searchIcon();
}; // 打开/关闭图标盒子
const searchIcon = () => {
if (state.timer) {
clearTimeout(state.timer);
}
state.timer = setTimeout(() => {
let iconList = [];
if (state.icon_search == "") {
state.showIconArr = state.iconArr;
} else {
state.iconArr.map((e) => {
if (e.name.indexOf(state.icon_search) !== -1) {
iconList.push(e);
}
});
state.showIconArr = iconList;
}
}, 500);
}; // 图标筛选
const setForm = (data) => {
if (data.level && data.level == 1) {
state.topLevel = true;
} else {
state.topLevel = false;
if (data.level == 2) {
state.groupFlag = false;
} else {
state.groupFlag = true;
}
}
state.typeDisFlag = data.p_menu_type;
if (data.id) {
// 编辑状态
state.radioDisabled = true;
} else {
state.radioDisabled = false;
}
state.disabledSwitch = data.built_in && data.built_in == 1 ? true : false;
state.menuForm = Object.assign(state.menuForm, data);
}; // 表单赋值
const clearIcon = () => {
state.menuForm.menu_logo = "";
}; // 清除选中的图标
const changeIcon = (e) => {
state.menuForm.menu_logo = e.icon;
state.iconFlag = false;
state.icon_search = "";
searchIcon();
}; // 选中图标
onMounted(() => {
// 处理图标数据
allIconArr.forEach((e) => {
state.iconArr.push({
icon: e,
name: e.replace("bg-ic-", ""),
});
});
state.showIconArr = state.iconArr;
getGroupList();
getSystemGroup();
});
const {
menuForm,
formRules,
groupList,
iconFlag,
showIconArr,
icon_search,
radioDisabled,
topLevel,
typeDisFlag,
groupFlag,
systemGroupList,
disabledSwitch,
} = toRefs(state);
defineExpose({ setForm, menuForm, menuRef });
</script>
<style lang="scss" scoped>
.menu_form {
.el-form-item {
margin-bottom: 24px;
:deep().el-form-item__label {
padding: 0;
margin-right: 8px;
}
.el-form-item__content {
.el-input,
.el-select {
width: 100%;
}
.bg-switch-ele {
width: 52px;
height: 24px;
:deep().el-switch__core {
width: 100%;
height: 24px;
.el-switch__inner,
.el-switch__action {
top: 3px;
}
}
}
.iconBox {
width: 100%;
display: flex;
padding: 0 12px;
height: 36px;
line-height: 36px;
justify-content: space-between;
border: 1px solid #dadee7;
border-radius: 4px;
cursor: pointer;
.bgIcon {
color: #202531;
width: 8px;
margin-top: 11px;
}
.clearIcon {
margin-right: 8px;
width: 14px;
display: none;
}
&:hover,
&:focus,
&:active {
border-color: #2b4695;
.clearIcon {
display: inline-block;
}
}
&:hover {
border-color: #a9b1c7;
}
}
.icon_box {
width: 100%;
height: 236px;
box-shadow: 0px 4px 12px 0px rgba(18, 30, 63, 0.1);
border-radius: 4px;
border: solid 1px #e6e9ef;
margin-top: 10px;
padding: 12px 0px 12px 8px;
.input_box {
margin-right: 8px;
}
.icon_content {
width: 100%;
height: 168px;
display: flex;
flex-wrap: wrap;
margin-top: 8px;
align-content: flex-start;
.icon_item {
display: inline-block;
border: 1px solid #dadee7;
border-radius: 3px;
width: 182px;
height: 36px;
margin: 0 8px 8px 0;
color: #202531;
padding: 0 16px;
line-height: 36px;
cursor: pointer;
&:hover,
&:active {
background-color: #eaedf5;
border-color: #95a3ca;
}
&:nth-child(4n) {
margin-right: 0;
}
.selectBgIcon {
width: 14px;
margin-right: 10px;
}
span {
display: inline-block;
width: 100%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
}
}
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="page_content">
<div class="inner-container apaas_scroll_min">
<bg-form-gap title="注册信息"></bg-form-gap>
<bg-detail-table2 class="register_info" :list="instanceData.approveBasicInfo">
<!-- <template v-slot:approvalStatus="{ data }">
<p class="detail-module">
<span>{{ data.label }}</span>
<span>{{ data.value }}</span>
</p>
</template> -->
</bg-detail-table2>
<bg-form-gap title="审批"></bg-form-gap>
<div class="approve">
<div class="header">
<div class="left">
<span class="approve_person">平台运营者审批</span>
<span class="approve_status">待审批</span>
</div>
<div class="right">
<span class="label">申请时间:</span>
<span class="value">{{ instanceData.approveBasicInfo[5].value }}</span>
</div>
</div>
<el-form
ref="approveFormRef"
:model="instanceData.formData"
:rules="instanceData.rules"
label-width="80px"
class="approveForm">
<el-form-item label="审批单位">
<el-input disabled v-model="userInfo.organization_name" maxlength="20" />
</el-form-item>
<el-form-item label="审批人">
<el-input disabled v-model="userInfo.contact_name" maxlength="20" />
</el-form-item>
<el-form-item label="审批结果" prop="status">
<el-radio-group v-model="instanceData.formData.status">
<el-radio :label="1"> 通过 </el-radio>
<el-radio :label="2"> 驳回 </el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="审批意见" prop="comments">
<el-input
placeholder="请输入"
rows="5"
maxlength="200"
v-model="instanceData.formData.comments"
show-word-limit
type="textarea" />
</el-form-item>
</el-form>
</div>
</div>
<div class="operate_btns">
<el-button type="default" @click="cancel"> 取消 </el-button>
<el-button type="primary" @click="save"> 保存 </el-button>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, onBeforeMount, computed, ref } from "vue";
import { useRouter, useRoute } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
// import { dateStringTransform } from "@/services/helper";
import store from "@/store";
const route = useRoute();
const router = useRouter();
const approveFormRef = ref(null);
const instanceData = reactive({
approveBasicInfo: [
{
key: "organization_name",
label: "所属组织",
value: "",
// slot: "approvalStatus",
},
{
key: "business_name",
label: "业务系统名称",
value: "",
},
{
key: "system_account",
label: "账号",
value: "",
},
{
key: "phone",
label: "手机号",
value: "",
},
{
key: "develop_name",
label: "开发厂商名称",
value: "",
},
{
key: "created_time",
label: "提交时间",
value: "",
},
{
key: "contact_name",
label: "联系人",
value: "",
},
{
key: "contact_phone",
label: "联系人手机号",
value: "",
},
{
key: "business_desc",
label: "业务系统描述",
value: "",
},
],
formData: {
status: "",
comments: "",
},
rules: {
status: [
{
required: true,
message: "请选择",
},
],
},
});
const userInfo = computed(() => {
return store.state.userInfo;
});
onBeforeMount(() => {
getApproveInfo();
});
const getApproveInfo = () => {
axios
.get(`/apaas/system/v5/user/approval/detail/${route.query.id}`)
.then((res) => {
if (res.data.code == 200) {
const result = res.data.data || {};
instanceData.approveBasicInfo.forEach((item) => {
// if (item.key === "created_time") {
// item.value = dateStringTransform(result[item.key]);
// return;
// }
item.value = result[item.key];
});
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
ElMessage.error(err);
});
};
//取消
const cancel = () => {
router.push("/develop/systemApproval");
};
//保存
const save = () => {
approveFormRef.value.validate((val) => {
if (val) {
const params = {
...instanceData.formData,
id: +route.query.id,
};
axios.post(`/apaas/system/v5/user/approval`, params).then((res) => {
if (res.data.code == "200") {
ElMessage.success("操作成功");
router.push("/develop/systemApproval");
} else {
ElMessage.error(res.data.data);
}
});
}
});
};
</script>
<style lang="scss" scoped>
.page_content {
position: relative;
padding: 24px 0 80px 24px;
.inner-container {
width: 100%;
height: 100%;
padding-bottom: 16px;
overflow: auto;
}
.register_info {
margin-bottom: 24px;
width: 960px;
}
.approve {
overflow: hidden;
width: 1127px;
height: 378px;
background-color: #ffffff;
border-radius: 4px;
border: solid 1px #e3e4ef;
.header {
display: flex;
justify-content: space-between;
height: 38px;
padding: 0 16px;
border-bottom: 1px solid #e3e4ef;
background-color: #f9fafc;
.left {
display: flex;
justify-content: space-between;
align-items: center;
width: 150px;
height: 100%;
.approve_person {
display: inline-block;
width: 98px;
font-size: 14px;
font-weight: 600;
color: #242c43;
}
.approve_status {
display: inline-block;
width: 46px;
height: 18px;
font-size: 12px;
text-align: center;
color: #e56600;
background-color: #fcf0e6;
border-radius: 3px;
border: solid 1px #f2b380;
}
}
.right {
display: flex;
justify-content: space-between;
align-items: center;
width: 210px;
height: 100%;
font-size: 14px;
color: #7784a6;
.value {
color: #242c43;
}
}
}
.approveForm {
padding: 24px 24px 0 24px;
:deep() .el-form-item {
margin-bottom: 24px;
}
:deep() .el-form-item__label,
:deep() .el-radio__label {
color: var(--el-text-color-primary);
}
}
}
.operate_btns {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 64px;
padding-right: 16px;
line-height: 64px;
text-align: right;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
& + .el-button {
margin-left: 16px;
}
}
}
}
.apaas_scroll_min::-webkit-scrollbar {
width: 6px;
height: 4px;
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="page_content">
<bg-form-gap title="注册信息"></bg-form-gap>
<bg-detail-table2 class="register_info" :list="instanceData.approveBasicInfo">
</bg-detail-table2>
<bg-form-gap title="审批信息"></bg-form-gap>
<bg-detail-table2 class="approve_info" :list="instanceData.approveInfo"> </bg-detail-table2>
</div>
</div>
</template>
<script setup>
import { reactive, onBeforeMount } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import { useRoute } from "vue-router";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { dateStringTransform } from "@/services/helper.js";
const route = useRoute();
const instanceData = reactive({
approveBasicInfo: [
{
key: "organization_name",
label: "所属组织",
value: "",
// slot: "approvalStatus",
},
{
key: "business_name",
label: "业务系统名称",
value: "",
},
{
key: "system_account",
label: "账号",
value: "",
},
{
key: "phone",
label: "手机号",
value: "",
},
{
key: "develop_name",
label: "开发厂商名称",
value: "",
},
{
key: "created_time",
label: "提交时间",
value: "",
},
{
key: "contact_name",
label: "联系人",
value: "",
},
{
key: "contact_phone",
label: "联系人手机号",
value: "",
},
{
key: "business_desc",
label: "业务系统描述",
value: "",
},
],
approveInfo: [
{
key: "status",
label: "审核状态",
value: "",
},
{
key: "approval_by",
label: "审核人",
value: "",
},
{
key: "approval_time",
label: "审批时间",
value: "",
},
{
key: "comments",
label: "审批意见",
value: "",
},
],
});
onBeforeMount(() => {
getApproveInfo();
});
const getApproveInfo = () => {
axios
.get(`/apaas/system/v5/user/approval/detail/${route.query.id}`)
.then((res) => {
if (res.data.code == 200) {
const result = res.data.data || {};
instanceData.approveBasicInfo.forEach((item) => {
// if (item.key === "created_time") {
// item.value = dateStringTransform(result[item.key]);
// return;
// }
item.value = result[item.key];
});
instanceData.approveInfo.forEach((item) => {
if (item.key === "status") {
item.value = ["待审批", "通过", "驳回"][result[item.key]];
return;
}
item.value = result[item.key];
});
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
ElMessage.error(err);
});
};
</script>
<style lang="scss" scoped>
.page_content {
position: relative;
padding: 24px 0 68px 24px;
.register_info,
.approve_info {
margin-bottom: 24px;
width: 960px;
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group @search="changeSearch" v-model="filter.search" placeholder="请输入业务系统名称、账号或所属组织">
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">审批状态</span>
<el-select v-model="filter.status" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in stateOptions"
:key="'pushOptions' + index"
:label="item.name"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">提交时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px" />
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<bg-table ref="bgTable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true">
<template v-slot:ability_name="{ row }">
<span @click="goDetail(row)" class="can_click_text">
{{ row.ability_name }}
<span v-if="row.include_mock_service" class="mock_tip"> mock </span>
</span>
</template>
<template v-slot:ability_state="{ row }">
<span class="circle" :class="'bgc_' + row.ability_state"></span>
{{ ["待上架", "已上架", "已下架", "上架中", "下架中"][row.ability_state] }}
</template>
<template v-slot:action="{ row }">
<bg-table-btns2>
<bg-table-btn v-if="row.status === 0" @click="approve(row)">审批</bg-table-btn>
<bg-table-btn v-else @click="approveDetail(row)">审批详情</bg-table-btn>
</bg-table-btns2>
</template>
</bg-table>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
<el-dialog class="dialog_box" title="删除" v-model="dialogDelete" width="400px">
<div style="text-align: left">
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;确定要删除数据吗?数据删除后不能恢复,请谨慎操作
</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
<el-dialog class="dialog_box" title="提示" v-model="cacheDialog" width="400px">
<div style="font-size: 16px; color: #404a62">你有未提交的服务,是否继续编辑该服务?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="cancelCache">取 消</el-button>
<el-button type="primary" @click="confirmCache">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, computed, onBeforeMount } from "vue";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const router = useRouter();
const state = reactive({
filter: {
search: "",
status: "",
time: [],
page: 1,
limit: 10,
},
stateOptions: [
{
name: "全部",
value: "",
},
{
name: "待审批",
value: 0,
},
{
name: "通过",
value: 1,
},
{
name: "驳回",
value: 2,
},
],
headers: [
{
label: "业务系统名称",
prop: "business_name",
minWidth: 120,
},
{
label: "账号",
prop: "system_account",
minWidth: 120,
},
{
label: "所属组织",
prop: "organization_name",
width: 200,
},
{
label: "审批状态",
prop: "status",
width: 120,
},
{
label: "提交时间",
prop: "created_time",
minWidth: 180,
},
{
label: "操作",
prop: "action",
width: 180,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
actionRow: {},
dialogDelete: false,
cacheDialog: false,
});
const { filter, stateOptions, headers, tableRows, tableTotal, dialogDelete, cacheDialog } = toRefs(state);
const cancelCache = () => {
axios
.get(`/apaas/service/v5/ability/clear/cach?abilityType=0`)
.then((res) => {
if (res.data.code == 200) {
state.cacheDialog = false;
router.push("/ability-register/add");
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
router.push("/ability-register/add");
});
}; // 取消按钮,清空缓存跳转注册页
const confirmCache = () => {
state.cacheDialog = false;
router.push("/ability-register/add");
}; // 确定按钮,直接跳转注册页
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
};
const changePage = (page) => {
state.filter.page = page;
getTableRows();
};
const getTableRows = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
if (!params.status.toString()) delete params.status;
axios
.get(`/apaas/system/v5/user/approval/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
const filterAction = () => {
changePage(1);
};
const changeSize = (limit) => {
state.filter.limit = limit;
changePage(1);
};
const filterClear = () => {
state.filter = {
search: "",
status: 0,
time: [],
page: 1,
limit: 10,
};
changePage(1);
};
const approve = (row) => {
router.push({
path: "/develop/systemApproval/approval",
query: {
id: row.id,
},
});
};
const approveDetail = (row) => {
router.push({
path: "/develop/systemApproval/detail",
query: {
id: row.id,
},
});
};
const deleteData = () => {
axios.delete(`/apaas/service/v5/ability/${state.actionRow.id}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.dialogDelete = false;
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
};
const goDetail = (row) => {
router.push({
path: "/ability-manage/real-list/detail",
query: {
id: row.id,
},
});
};
onBeforeMount(() => {
getTableRows();
});
</script>
<style lang="scss" scoped>
.page_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
padding: 0 24px;
min-height: 100%;
.main_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex-grow: 1;
width: 100%;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
padding: 0 0 16px 0;
margin-bottom: 16px;
position: relative;
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
.filter_item {
display: flex;
align-items: center;
.filter_title {
width: 84px;
white-space: pre-wrap;
font-weight: normal;
}
}
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
padding: 0 16px;
.el-table {
flex: 1;
.circle {
display: inline-block;
width: 6px;
height: 6px;
border-radius: 3px;
margin-right: 8px;
transform: translateY(-2px);
}
.bgc_1 {
background-color: #48ad97;
}
.bgc_2 {
background-color: #d75138;
}
.bgc_3 {
background-color: #3759be;
}
.bgc_4 {
background-color: #ea7d19;
}
.bgc_0 {
background-color: #9e9e9e;
}
.mock_tip {
display: inline-block;
margin-left: 4px;
width: 43px;
height: 20px;
font-size: 12px;
color: #2b4695;
text-align: center;
background-color: #eff2fa;
border-radius: 3px;
border: solid 1px #b0bee8;
}
}
}
}
}
</style>
<template>
<el-form :model="formData" :rules="formRules" ref="form" style="max-width: 1048px" class="form">
<el-form-item label="banner名称" prop="banner_name">
<el-input v-model.trim="formData.banner_name" show-word-limit maxlength="30" />
</el-form-item>
<el-form-item label="链接地址(选填)" prop="link_address">
<el-input v-model.trim="formData.link_address" />
</el-form-item>
<el-form-item label="上传banner图片" prop="banner_image">
<bg-upload-image
v-model="formData.banner_image"
:fileSize="1"
:showTips="true"
customTips="请选择图片上传:大小1920 * 464像素支持jpg、jpeg、png格式,图片需小于1MB"
:limit="1"
listType="picture-card"
@change="changeLogo"
:accept="['.jpg', '.jpeg', '.png']"></bg-upload-image>
</el-form-item>
<el-form-item label="是否启用" prop="state" class="state_form_item">
<bg-switch :labels="['否', '是']" :values="[0, 1]" v-model="formData.state"></bg-switch>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
const router = useRouter();
const route = useRoute();
const form = ref(null);
const state = reactive({
formData: {
banner_name: "",
link_address: "",
banner_image: [],
state: 1,
},
formRules: {
banner_name: [{ required: true, message: "请输入banner名称", trigger: "blur" }],
link_address: [{ max: 8182, message: "最长可输入8182个字符", trigger: "blur" }],
banner_image: [{ required: true, message: "请上传banner图", trigger: "change" }],
state: [{ required: true, message: "请选择启用状态", trigger: "change" }],
},
});
const changeLogo = (value) => {
if (value && value.length > 0) {
form.value.clearValidate("banner_image");
}
};
const emit = defineEmits(["action"]);
const submitForm = async () => {
if (!form) return;
await form.value.validate((valid, fields) => {
if (valid) {
emit("action", state.formData);
} else {
emit("action", null);
}
});
};
const clearForm = () => {
if (!form) return;
form.value.resetFields();
};
const setForm = (data) => {
Object.assign(state.formData, data);
};
onBeforeMount(() => {});
const { formData, formRules } = toRefs(state);
defineExpose({ submitForm, clearForm, setForm });
</script>
<style lang="scss" scoped>
.form {
.el-form-item {
display: block;
}
.state_form_item {
display: flex;
}
:deep().bg-upload {
.el-upload--picture-card {
width: 800px;
height: 194px;
background-color: #fff;
border: 1px solid #dadee7;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 800px;
height: 194px;
}
}
}
</style>
<!-- banner管理 -->
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="content_top">
<banner-form ref="bannerFormRef" @action="submitFormData"></banner-form>
</div>
<div class="content_bottom">
<div>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">保存</el-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import bannerForm from "./banner-form.vue";
const router = useRouter();
const route = useRoute();
const bannerFormRef = ref(null);
const state = reactive({
formData: {
banner_name: "",
link_address: "",
banner_image: [],
state: 1,
},
});
const getDetail = (id) => {
axios
.get(`/apaas/system/v5/banner/${id}`)
.then((res) => {
if (res.data.code == 200) {
const data = res.data.data;
bannerFormRef.value.setForm({
banner_name: data.banner_name,
link_address: data.link_address,
banner_image: data.banner_image ? [{ url: data.banner_image }] : [],
state: data.state,
});
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const submit = () => {
bannerFormRef.value.submitForm();
};
const submitFormData = (data) => {
if (data) {
Object.assign(state.formData, data);
console.log(state.formData);
if (route.query.id) {
// 编辑
let params = {
...state.formData,
id: +route.query.id,
};
params.banner_image = params.banner_image && params.banner_image.length > 0 ? params.banner_image[0].url : "";
axios.put(`/apaas/system/v5/banner`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
router.go(-1);
} else {
ElMessage.error(res.data.data);
}
});
} else {
let params = {
...state.formData,
};
params.banner_image = params.banner_image && params.banner_image.length > 0 ? params.banner_image[0].url : "";
axios.post(`/apaas/system/v5/banner`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
router.go(-1);
} else {
ElMessage.error(res.data.data);
}
});
}
}
};
const cancel = () => {
router.go(-1);
};
onBeforeMount(() => {
if (route.query.id) {
getDetail(route.query.id);
}
});
const {} = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
// padding: 24px;
.content_top {
flex: 1;
display: flex;
flex-direction: column;
overflow: auto;
padding: 24px;
}
.content_bottom {
height: 68px;
line-height: 68px;
text-align: right;
padding: 0 20px;
border-top: 1px solid #e6e9ef;
.el-button {
width: 92px;
}
}
}
}
</style>
<!-- banner管理 -->
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group :showSearch="false">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="addBanner">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<div class="table bg-scroll">
<bg-table ref="Bgtable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true">
<template v-slot:updated_time="{ row }">
{{ row.updated_time.split("+")[0].replace("T", " ").replace("Z", " ") }}
</template>
<template v-slot:state="{ row }">
<bg-switch
@click="changeUseRow(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.state"></bg-switch>
</template>
<template v-slot:action="{ row }">
<bg-table-btns2 :limit="3">
<bg-table-btn @click="edit_row(row)" :disabled="row.state == 1">编辑</bg-table-btn>
<bg-table-btn @click="delete_row(row)" :disabled="row.state == 1">删除</bg-table-btn>
<bg-table-btn @click="moveRow(row, 1)" :disabled="!row.canMoveUp">上移</bg-table-btn>
<bg-table-btn @click="moveRow(row, 2)" :disabled="!row.canMoveDown">下移</bg-table-btn>
</bg-table-btns2>
</template>
</bg-table>
</div>
</div>
</div>
<!-- 删除弹窗 -->
<el-dialog
class="dialog_box"
title="提示"
v-model="dialogDelete"
width="400px"
:before-close="
() => {
dialogDelete = false;
}
">
<div>确定要删除吗?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const router = useRouter();
const Bgtable = ref(null);
const state = reactive({
headers: [
{
label: "banner名称",
prop: "banner_name",
},
{
label: "链接地址",
prop: "link_address",
},
{
label: "创建人",
prop: "created_by",
},
{
label: "更新时间",
prop: "updated_time",
},
{
label: "是否启用",
prop: "state",
},
{
label: "操作",
prop: "action",
width: 176,
fixed: "right",
},
],
tableRows: [],
dialogDelete: false,
actionRow: null,
});
const getRoleRows = () => {
axios.get(`/apaas/system/v5/banner/list`).then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
if (state.tableRows.length > 0) {
state.tableRows.forEach((e) => {
e.canMoveUp = true;
e.canMoveDown = true;
});
state.tableRows[0].canMoveUp = false;
state.tableRows[state.tableRows.length - 1].canMoveDown = false;
}
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取列表
const addBanner = () => {
router.push("/home-config/banner/add");
};
const edit_row = (row) => {
router.push({
path: "/home-config/banner/edit",
query: {
id: row.id,
},
});
};
const delete_row = (row) => {
state.actionRow = row;
state.dialogDelete = true;
};
const deleteData = () => {
axios.delete(`/apaas/system/v5/banner/${state.actionRow.id}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.dialogDelete = false;
getRoleRows();
} else {
ElMessage.error(res.data.data);
}
});
};
const moveRow = (row, type) => {
let index;
state.tableRows.forEach((e, i) => {
if (e.id == row.id) {
index = i;
}
});
let nextRow;
if (type == 1) {
// 上移
nextRow = state.tableRows[index - 1];
} else {
// 下移
nextRow = state.tableRows[index + 1];
}
let params = [
{
id: row.id,
sort: nextRow.sort,
},
{
id: nextRow.id,
sort: row.sort,
},
];
axios
.post(`/apaas/system/v5/banner/sort`, [...params])
.then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getRoleRows();
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const changeUseRow = (row) => {
let params = {
id: row.id,
state: row.state,
};
axios.post(`/apaas/system/v5/banner/status`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getRoleRows();
} else {
ElMessage.error(res.data.data);
row.state = row.state == 0 ? 1 : 0;
}
});
};
onBeforeMount(() => {
getRoleRows();
});
const { filter, headers, tableRows, tableTotal, dialogDelete } = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
.table_container {
height: calc(100% - 70px);
padding: 0 16px;
.table {
max-height: calc(100% - 64px);
}
}
}
}
</style>
<!-- banner管理 -->
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="content_top">
<el-form :model="formData" :rules="formRules" ref="formRef" style="max-width: 1048px" class="form">
<el-form-item label="公司名称" prop="company_name">
<el-input v-model.trim="formData.company_name" placeholder="请输入公司名称" show-word-limit maxlength="50" />
</el-form-item>
<el-form-item label="公司地址" prop="company_address">
<el-input v-model.trim="formData.company_address" placeholder="请输入公司地址" show-word-limit maxlength="100" />
</el-form-item>
<el-form-item label="公司logo" prop="logo">
<bg-upload-image
v-model="formData.logo"
:fileSize="500"
:fileSizeUnit="'KB'"
:showTips="true"
customTips="请选择图片上传:大小80 * 80像素支持jpg、jpeg、png格式,图片需小于500KB"
:limit="1"
listType="picture-card"
:accept="['.jpg', '.jpeg', '.png']"></bg-upload-image>
</el-form-item>
<el-form-item label="公司电话" prop="contact_phone">
<el-input v-model.trim="formData.contact_phone" placeholder="请输入公司电话" show-word-limit maxlength="20" />
</el-form-item>
</el-form>
</div>
<div class="content_bottom">
<div>
<el-button type="primary" @click="submit">保存</el-button>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const router = useRouter();
const route = useRoute();
const formRef = ref(null);
const checkPhone = (rule, value, callback) => {
let reg = /^((1[0-9]{10})|(((([0-9]{3}-)?[0-9]{8})|(([0-9]{4}-)?[0-9]{7}))(-[0-9]{1,4})?))$/;
if (value !== "") {
console.log(1);
if (!reg.test(value)) {
console.log(2);
callback(new Error("电话号码格式不正确,请重新输入"));
} else {
console.log(3);
callback();
}
} else {
callback();
}
};
const state = reactive({
formData: {
company_name: "",
company_address: "",
logo: [],
contact_phone: "",
},
formRules: {
company_name: [
{ required: true, message: "请输入公司名称", trigger: "blur" },
{ max: 50, message: "长度最大为50个字符", trigger: "blur" },
],
company_address: [],
logo: [],
contact_phone: [{ validator: checkPhone, trigger: "blur" }],
},
});
const getFootInfo = () => {
axios
.get(`/apaas/system/v5/config/get`)
.then((res) => {
if (res.data.code == 200) {
const data = res.data.data;
state.formData = {
company_name: data.company_name,
company_address: data.company_address,
logo: data.logo ? [{ url: data.logo }] : [],
contact_phone: data.contact_phone,
};
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const submit = () => {
formRef.value.validate((valid, fields) => {
if (valid) {
let params = {
...state.formData,
logo: state.formData.logo && state.formData.logo.length > 0 ? state.formData.logo[0].url : "",
};
axios.post(`/apaas/system/v5/config/set`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
});
}
});
};
onBeforeMount(() => {
getFootInfo();
});
const { formData, formRules } = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
// padding: 24px;
.content_top {
flex: 1;
display: flex;
flex-direction: column;
overflow: auto;
padding: 24px;
.form {
.el-form-item {
display: block;
:deep().bg-upload {
.el-upload--picture-card {
width: 80px;
height: 80px;
background-color: #fff;
border: 1px solid #dadee7;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 80px;
height: 80px;
}
}
}
}
}
.content_bottom {
height: 68px;
line-height: 68px;
text-align: right;
padding: 0 20px;
border-top: 1px solid #e6e9ef;
.el-button {
width: 92px;
}
}
}
}
</style>
<!-- banner管理 -->
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="content_top bg-scroll">
<type-form
ref="typeFormRef"
:businessList="businessList"
@action="submitFormData"
@changeType="changeType"></type-form>
<div class="ability_select">
<div class="title">
<span>已选择能力({{ selectedItemList.length }}/9)</span>
<el-tooltip class="box-item" effect="light" content="最少需选择6个能力" placement="top">
<bg-icon icon="#bg-ic-circle-warning" style="color: #7784a6"></bg-icon>
</el-tooltip>
</div>
<div class="select_arr">
<ability-list
:list="selectedItemList"
:flag="true"
@openBox="openDialog"
@deleteItem="deleteItem"
@move="move"></ability-list>
</div>
</div>
<div class="state_box">
<span>是否启用</span>
<bg-switch :labels="['否', '是']" :values="[0, 1]" v-model="formData.state"></bg-switch>
</div>
</div>
<div class="content_bottom">
<div>
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="submit">保存</el-button>
</div>
</div>
</div>
<el-dialog
class="select_dialog"
title="选择能力"
v-model="selectDialog"
width="1220px"
:before-close="cancelSelectDialog">
<div class="select_box">
<div class="topFilter">
<div class="right">
<el-select
v-model="dialogFilter.openness"
placeholder="请选择开放程度"
style="width: 240px"
multiple
collapse-tags>
<el-option
v-for="(item, index) in typeList"
:key="'typeList' + index"
:label="item.name"
:value="item.id">
</el-option>
</el-select>
<el-cascader
v-model="dialogFilter.organization_id"
:options="orgList"
placeholder="请选择组织"
:props="{
expandTrigger: 'hover',
label: 'name',
value: 'id',
multiple: true,
emitPath: false,
checkStrictly: true,
children: 'Child',
}"
:clearable="true"
collapse-tags
style="width: 240px">
<template #default="{ data }">
<span>{{ data.name }}</span>
</template>
</el-cascader>
<el-input
v-model.trim="dialogFilter.keyword"
placeholder="请输入能力/开发商名称"
style="width: 240px"
:prefix-icon="Search" />
<el-button type="primary" @click="searchAction">查询</el-button>
<el-button type="default" @click="clearAction">重置</el-button>
</div>
</div>
<div class="table_box">
<div class="collation">
<div>
<span
class="collation-text"
:class="{ current: dialogFilter.collation == item.value }"
v-for="(item, index) in collationArr"
:key="'collationArr' + index"
@click="changeCollation(item.value)"
>{{ item.name }}</span
>
</div>
<div>
<span>已选择</span>
<span class="num"
>({{ selectedItemList.length + toBeAddedItemList.length }}/9)</span
>
<span></span>
<span class="can_click_text" @click="clearSelection">清空</span>
</div>
</div>
<div class="user_table bg-scroll">
<ability-list
ref="abilityListRef"
:list="list"
:loading="abilityLoading"
:flag="false"
@change-selected-list="changeSelectedList"></ability-list>
</div>
</div>
</div>
<template v-slot:footer>
<bg-pagination
:page="dialogFilter.page"
:size="dialogFilter.limit"
:total="total"
@change-page="changePage"
@change-size="changeSize"
:pageSizes="[15, 30, 50]">
</bg-pagination>
<div class="apaas_button">
<el-button type="default" @click="cancelSelectDialog">取 消</el-button>
<el-button type="primary" @click="confirm">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import typeForm from "./type-form.vue";
import abilityList from "@/components/ability-list.vue";
const router = useRouter();
const route = useRoute();
const typeFormRef = ref(null);
const abilityListRef = ref(null);
const state = reactive({
formData: {
business_type_id: "",
logo: [],
state: 1,
},
selectedItemList: [],
selectDialog: false,
dialogFilter: {
openness: [],
organization_id: [],
keyword: "",
limit: 15,
page: 1,
collation: 0,
},
orgList: [],
typeList: [],
total: 0,
collationArr: [
{
name: "综合排序",
value: 0,
},
{
name: "最新更新",
value: 1,
},
{
name: "最高人气",
value: 2,
},
{
name: "最好评价",
value: 3,
},
],
businessList: [],
list: [],
abilityLoading: false,
toBeAddedItemList: [],
});
const getDetail = (id) => {
axios
.get(`/apaas/system/v5/recommend/ability/detail/${id}`)
.then((res) => {
if (res.data.code == 200) {
const data = res.data.data;
typeFormRef.value.setForm({
business_type_id: data.business_type_id,
logo: data.logo ? [{ url: data.logo }] : [],
});
state.selectedItemList = [];
state.toBeAddedItemList = [];
state.formData.state = data.state;
state.formData.business_type_id = data.business_type_id;
state.selectedItemList = data.ability_list || [];
state.selectedItemList.forEach((e, i) => {
e.canDown = true;
e.canUp = true;
if (i == 0) {
e.canUp = false;
}
if (i == state.selectedItemList.length - 1) {
e.canDown = false;
}
});
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const getOrgList = () => {
axios.get(`/apaas/system/v5/org/tree`).then((res) => {
if (res.data.code == 200) {
state.orgList = res.data.data || [];
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取组织列表
const changeType = (val) => {
state.formData.business_type_id = val;
state.selectedItemList = [];
state.toBeAddedItemList = [];
};
const getAbilityList = () => {
state.abilityLoading = true;
let params = {
...state.dialogFilter,
business_type: state.formData.business_type_id,
};
params.organization_id =
params.organization_id && params.organization_id.length > 0
? params.organization_id.join(",")
: "";
params.openness = params.openness && params.openness.length > 0 ? params.openness.join(",") : "";
axios.get(`/apaas/system/v5/recommend/ability/list`, { params }).then((res) => {
if (res.data.code == 200) {
let selecteItemIdList = state.selectedItemList.map((e) => e.id);
state.list = res.data.data.ability_list || [];
state.list.forEach((item) => {
if (selecteItemIdList.indexOf(item.id) > -1) {
item.isSelected = true;
}
});
state.total = res.data.data.ability_total;
}
state.abilityLoading = false;
});
};
const openDialog = () => {
state.selectDialog = true;
clearAction();
};
const deleteItem = (item) => {
let selecteItemIdList = state.selectedItemList.map((e) => e.id);
state.selectedItemList.splice(selecteItemIdList.indexOf(item.id), 1);
moveAbleChanging();
};
const move = (temp) => {
let id = temp.item.id;
let index = null;
state.selectedItemList.forEach((e, i) => {
if (e.id == id) {
index = i;
}
});
if (temp.type == 1) {
state.selectedItemList[index] = state.selectedItemList[index + 1];
state.selectedItemList[index + 1] = temp.item;
} else {
state.selectedItemList[index] = state.selectedItemList[index - 1];
state.selectedItemList[index - 1] = temp.item;
}
state.selectedItemList.forEach((e, i) => {
if (e != "") {
e.canDown = true;
e.canUp = true;
if (i == 0) {
e.canUp = false;
}
if (i == state.selectedItemList.length - 1) {
e.canDown = false;
}
}
});
};
const changePage = (val) => {
state.dialogFilter.page = val;
getAbilityList();
};
const changeSize = (val) => {
state.dialogFilter.limit = val;
changePage(val);
};
const changeCollation = (val) => {
state.dialogFilter.collation = val;
changePage(val);
};
const clearSelection = () => {
state.toBeAddedItemList = [];
abilityListRef.value.itemRefs.forEach((item) => {
item.checked = false;
});
};
const searchAction = () => {
changePage(1);
};
const clearAction = () => {
state.dialogFilter = {
openness: [],
organization_id: [],
keyword: "",
collation: 0,
limit: 15,
};
changePage(1);
};
const changeSelectedList = (item, checked) => {
if (checked) {
state.toBeAddedItemList.push(item);
} else {
let _toBeAddedItemIdList = state.toBeAddedItemList.map((e) => e.id);
state.toBeAddedItemList.splice(_toBeAddedItemIdList.indexOf(item.id), 1);
}
moveAbleChanging();
};
const moveAbleChanging = () => {
state.selectedItemList.forEach((e, i) => {
e.canDown = true;
e.canUp = true;
if (i == 0) {
e.canUp = false;
}
if (i == state.selectedItemList.length - 1) {
e.canDown = false;
}
});
};
const submit = () => {
typeFormRef.value.submitForm();
};
const cancelSelectDialog = () => {
state.selectDialog = false;
state.toBeAddedItemList = [];
state.list = [];
};
const confirm = () => {
if (
state.toBeAddedItemList.length > 9 ||
state.selectedItemList.length + state.toBeAddedItemList.length > 9
) {
ElMessage.error("最多可选择9个能力");
return;
}
const _newItemList = [];
state.toBeAddedItemList.forEach((toBeAddedItem) => {
const hasItem = state.selectedItemList.some(
(selectedItem) => toBeAddedItem.id === selectedItem.id
);
if (!hasItem) {
_newItemList.push(toBeAddedItem);
}
});
state.selectedItemList.push(..._newItemList);
state.toBeAddedItemList.length = 0;
moveAbleChanging();
cancelSelectDialog();
};
const submitFormData = (data) => {
if (state.selectedItemList.length < 6) {
ElMessage.error("最少选择6个能力");
return;
}
if (data) {
Object.assign(state.formData, data);
if (route.query.id) {
// 编辑
let abilitys = [];
state.selectedItemList.forEach((e) => {
abilitys.push(e.ability_id);
});
let params = {
...state.formData,
id: +route.query.id,
abilitys: abilitys,
};
delete params.business_type_id;
params.logo = params.logo && params.logo.length > 0 ? params.logo[0].url : "";
axios.post(`/apaas/system/v5/recommend/ability/update`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
router.go(-1);
} else {
ElMessage.error(res.data.data);
}
});
} else {
let abilitys = [];
state.selectedItemList.forEach((e) => {
abilitys.push(e.ability_id);
});
let params = {
...state.formData,
abilitys: abilitys,
};
params.logo = params.logo && params.logo.length > 0 ? params.logo[0].url : "";
axios.post(`/apaas/system/v5/recommend/ability/add`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
router.go(-1);
} else {
ElMessage.error(res.data.data);
}
});
}
}
};
const cancel = () => {
router.go(-1);
};
const getTypeList = () => {
axios.get("/apaas/system/v5/dictionary/alllist").then((res) => {
if (res.data.code == 200) {
let data = res.data.data || [];
data.forEach((e) => {
if (e.classify_id == "eb9c7d70-c123-42b7-8e61-dde1b022b669") {
state.businessList = e.list || [];
}
if (e.classify_id == "949a1138-4995-464e-97a9-424d097eb271") {
state.typeList = e.list || [];
}
});
} else {
ElMessage.error(res.data.data);
}
});
};
onBeforeMount(() => {
getTypeList();
getOrgList();
if (route.query.id) {
getDetail(route.query.id);
}
});
const {
formData,
selectedItemList,
selectDialog,
dialogFilter,
typeList,
total,
orgList,
collationArr,
list,
abilityLoading,
toBeAddedItemList,
businessList,
} = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
// padding: 24px;
.content_top {
flex: 1;
display: flex;
flex-direction: column;
overflow: auto;
padding: 30px 40px 20px;
.ability_select {
.title {
font-size: 14px;
color: #202531;
margin-bottom: 6px;
&::before {
content: "*";
color: var(--el-color-danger);
margin-right: 4px;
}
}
.select_arr {
width: 1216px;
transform: translateX(-16px);
// .empty_card {
// cursor: pointer;
// width: 384px;
// height: 242px;
// border: 1px dashed #dadee7;
// border-radius: 4px;
// margin-bottom: 24px;
// color: #dadee7;
// text-align: center;
// line-height: 242px;
// font-size: 48px;
// font-weight: 100;
// }
}
}
.state_box {
font-size: 14px;
span {
margin-right: 16px;
}
}
}
.content_bottom {
height: 68px;
line-height: 68px;
text-align: right;
padding: 0 20px;
border-top: 1px solid #e6e9ef;
.el-button {
width: 92px;
}
}
}
.select_dialog {
.select_box {
width: 100%;
height: 680px;
.topFilter {
height: 68px;
padding: 16px;
border-bottom: 1px solid #e6e9ef;
display: flex;
font-size: 14px;
align-items: center;
flex-direction: row-reverse;
// .left {
// width: 240px;
// padding-left: 15px;
// color: #404a62;
// .num {
// color: #202531;
// font-weight: 700;
// margin: 0 3px;
// }
// }
.right {
width: 912px;
display: flex;
align-items: center;
justify-content: space-between;
.el-button {
margin: 0;
width: 64px;
}
}
}
.table_box {
padding: 10px 0;
height: calc(100% - 68px);
background-color: #f8f9fb;
.collation {
width: 100%;
display: flex;
justify-content: space-between;
padding: 6px 16px 16px;
font-size: 14px;
color: #202531;
.collation-text {
font-size: 14px;
color: #404a62;
margin-right: 32px;
position: relative;
cursor: pointer;
&::after {
content: "";
display: block;
position: absolute;
top: 2px;
right: -16px;
width: 1px;
cursor: auto;
height: 16px;
background-color: #cbced7;
}
&:nth-last-of-type(1)::after {
display: none;
}
}
.current {
color: #3759be !important;
}
.can_click_text {
margin-left: 24px;
}
}
.user_table {
max-height: calc(100% - 31px);
// padding: 0 16px;
// padding-left: 16px;
}
}
}
.bg-pagination {
float: left;
margin-top: 0;
}
}
}
</style>
<style>
.select_dialog {
margin-top: 8vh;
}
.select_dialog .el-dialog__body {
padding: 0;
}
.select_dialog .el-dialog__footer {
padding: 16px;
}
</style>
<template>
<el-form :model="formData" :rules="formRules" ref="form" style="max-width: 1048px" class="form">
<el-form-item label="能力类型" prop="business_type_id">
<el-select
v-model="formData.business_type_id"
placeholder="请选择能力类型"
style="width: 100%"
@change="changeType"
:disabled="!!route.query.id">
<el-option v-for="item in props.businessList" :key="item.dict_id" :label="item.name" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="上传图标" prop="logo">
<bg-upload-image
v-model="formData.logo"
:fileSize="500"
:fileSizeUnit="'KB'"
:showTips="true"
customTips="请选择图片上传:大小36 * 36像素支持jpg、jpeg、png格式,图片需小于500KB"
:limit="1"
listType="picture-card"
@change="changeLogo"
:accept="['.jpg', '.jpeg', '.png']"></bg-upload-image>
</el-form-item>
</el-form>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRoute, useRouter } from "vue-router";
const router = useRouter();
const route = useRoute();
const form = ref(null);
const props = defineProps({
businessList: {
type: Array,
default: [],
},
});
const state = reactive({
formData: {
business_type_id: "",
logo: [],
},
formRules: {
business_type_id: [{ required: true, message: "请选择业务类型", trigger: "change" }],
logo: [{ required: true, message: "请上传图标", trigger: "change" }],
},
});
const emit = defineEmits(["action", "changeType"]);
const changeLogo = (value) => {
if (value && value.length > 0) {
form.value.clearValidate("logo")
}
}
const submitForm = async () => {
if (!form) return;
await form.value.validate((valid, fields) => {
if (valid) {
emit("action", state.formData);
} else {
emit("action", null);
}
});
};
const changeType = (val) => {
emit("changeType", val);
};
const clearForm = () => {
if (!form) return;
form.value.resetFields();
};
const setForm = (data) => {
Object.assign(state.formData, data);
};
onBeforeMount(() => {});
const { formData, formRules } = toRefs(state);
defineExpose({ submitForm, clearForm, setForm });
</script>
<style lang="scss" scoped>
.form {
.el-form-item {
display: block;
}
:deep().bg-upload {
.el-upload--picture-card {
width: 56px;
height: 56px;
background-color: #fff;
border: 1px solid #dadee7;
}
.el-upload-list--picture-card .el-upload-list__item {
width: 56px;
height: 56px;
}
}
}
</style>
<!-- 推荐管理 -->
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group :showSearch="false">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="addBanner">
<bg-icon style="font-size: 12px; color: #fff; margin-right: 8px" icon="#bg-ic-add"></bg-icon>
新增
</el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<div class="table bg-scroll">
<bg-table ref="Bgtable" :headers="headers" :rows="tableRows" :isIndex="true" :stripe="true">
<template v-slot:recommend_state="{ row }">
<bg-switch
@click="changeUseRow(row)"
:labels="['否', '是']"
:values="[0, 1]"
v-model="row.recommend_state"></bg-switch>
</template>
<template v-slot:action="{ row }">
<bg-table-btns2 :limit="3">
<bg-table-btn @click="edit_row(row)" :disabled="row.recommend_state == 1">编辑</bg-table-btn>
<bg-table-btn @click="delete_row(row)" :disabled="row.recommend_state == 1">删除</bg-table-btn>
<bg-table-btn @click="moveRow(row, 1)" :disabled="!row.canMoveUp">上移</bg-table-btn>
<bg-table-btn @click="moveRow(row, 2)" :disabled="!row.canMoveDown">下移</bg-table-btn>
</bg-table-btns2>
</template>
</bg-table>
</div>
</div>
</div>
<!-- 删除弹窗 -->
<el-dialog class="dialog_box" title="提示" v-model="dialogDelete" width="400px">
<div>确定要删除吗?</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="deleteData">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, onBeforeMount, nextTick } from "vue";
import axios from "../../../../request/http.js";
import { ElMessage } from "element-plus";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const router = useRouter();
const Bgtable = ref(null);
const state = reactive({
headers: [
{
label: "能力类型",
prop: "business_type_name",
},
{
label: "创建人",
prop: "created_user",
},
{
label: "更新时间",
prop: "created_time",
},
{
label: "是否启用",
prop: "recommend_state",
},
{
label: "操作",
prop: "action",
width: 176,
fixed: "right",
},
],
tableRows: [],
dialogDelete: false,
actionRow: null,
});
const getRoleRows = () => {
axios.get(`/apaas/system/v5/recommend/ability/classify/list`).then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
if (state.tableRows.length > 0) {
state.tableRows.forEach((e) => {
e.canMoveUp = true;
e.canMoveDown = true;
});
state.tableRows[0].canMoveUp = false;
state.tableRows[state.tableRows.length - 1].canMoveDown = false;
}
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取列表
const addBanner = () => {
router.push("/home-config/recommend/add");
};
const edit_row = (row) => {
router.push({
path: "/home-config/recommend/edit",
query: {
id: row.id,
},
});
};
const delete_row = (row) => {
state.actionRow = row;
state.dialogDelete = true;
};
const deleteData = () => {
let params = {
id: state.actionRow.id,
};
axios.delete(`/apaas/system/v5/recommend/ability/delete`, { data: params }).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
state.dialogDelete = false;
getRoleRows();
} else {
ElMessage.error(res.data.data);
}
});
};
const moveRow = (row, type) => {
let index;
state.tableRows.forEach((e, i) => {
if (e.id == row.id) {
index = i;
}
});
let nextRow;
if (type == 1) {
// 上移
nextRow = state.tableRows[index - 1];
} else {
// 下移
nextRow = state.tableRows[index + 1];
}
let params = [
{
id: row.id,
sort: nextRow.sort,
},
{
id: nextRow.id,
sort: row.sort,
},
];
axios
.post(`/apaas/system/v5/recommend/ability/sort`, [...params])
.then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getRoleRows();
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.log(err);
});
};
const changeUseRow = (row) => {
axios.get(`/apaas/system/v5/recommend/ability/state/${row.id}/${row.recommend_state}`).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
getRoleRows();
} else {
ElMessage.error(res.data.data);
row.recommend_state = row.recommend_state == 0 ? 1 : 0;
}
});
};
onBeforeMount(() => {
getRoleRows();
});
const { headers, tableRows, dialogDelete } = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
padding: 0 24px;
height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.main_container {
flex-grow: 1;
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
margin: 0 0 16px;
position: relative;
height: calc(100% - 62px);
.table_container {
height: calc(100% - 70px);
padding: 0 16px;
.table {
max-height: calc(100% - 64px);
}
}
}
}
</style>
<template> <template>
<div> <div></div>
</div>
</template> </template>
<script> <script>
export default { export default {
props: { props: {},
components: {},
},
components: {
},
data() { data() {
return { return {};
};
},
watch: {
},
computed: {
},
created() {
},
mounted() {
},
methods: {
}, },
watch: {},
computed: {},
created() {},
mounted() {},
methods: {},
}; };
</script> </script>
<style scoped> <style scoped></style>
</style>
<template>
<div class="detail_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="flex_row">
<div class="flex_left">
<div class="box">
<el-date-picker
style="width: 100%"
type="date"
value-format="YYYY-MM-DD"
v-model="date"
placeholder="请选择日期" />
<div class="type_station bg-scroll">
<div
class="type-box"
:class="{ 'current-type': selectedNodeContainer == item.value }"
@click="nodeClick(item)"
v-for="(item, index) in containerList"
:key="index"
:title="item.name">
{{ item.name }}
</div>
</div>
</div>
</div>
<div class="flex_right">
<div class="form-filter">
<el-form :model="filterForm" inline>
<el-form-item label="级别">
<el-select placeholder="全部" style="width: 160px" v-model="filterForm.level">
<el-option label="info" value="info"> </el-option>
<el-option label="warning" value="warning"> </el-option>
<el-option label="error" value="error"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="关键字">
<el-input
placeholder="请输入"
maxlength="100"
style="width: 240px"
v-model="filterForm.search"></el-input>
</el-form-item>
</el-form>
<div class="operate_btns">
<el-button type="primary" @click="search"> 查询 </el-button>
<el-button type="default" @click="reset"> 重置 </el-button>
</div>
</div>
<div class="search-result">
<div class="item-container apaas_scroll">
<p v-for="(item, index) in logList" :key="index">{{ item }}</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, watch, onBeforeMount, toRefs } from "vue";
import { ElMessage } from "element-plus";
import axios from "../../../../request/http.js";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const state = reactive({
date: "",
containerList: [], // 容器数据
selectedNodeContainer: "",
filterForm: {
level: "",
search: "",
},
logList: [],
});
watch(
() => state.date,
(val) => {
state.selectedNodeContainer = "";
getContainerList();
}
);
const nodeClick = (item) => {
state.selectedNodeContainer = item.value;
reset();
};
// 获取容器列表
const getContainerList = () => {
let params = {
date: state.date,
};
axios
.get("/apaas/system/v5/log/system/tree", { params })
.then((res) => {
if (res.data.code == 200) {
state.containerList = res.data.data || [];
state.selectedNodeContainer = state.containerList.length
? state.containerList[0].value
: "";
state.selectedNodeContainer ? reset() : (state.logList = []);
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.error(err);
});
};
//查询
const search = () => {
let params = {
index: state.selectedNodeContainer,
...state.filterForm,
};
axios
.get("/apaas/system/v5/log/system/log", { params })
.then((res) => {
if (res.data.code == 200) {
state.logList = (res.data.data || []).map((item) => JSON.stringify(item));
state.logList = state.logList.concat(state.logList);
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
console.error(err);
});
};
//重置
const reset = () => {
state.filterForm = {
level: "",
search: "",
};
search();
};
onBeforeMount(() => {
getContainerList();
});
const { date, containerList, selectedNodeContainer, filterForm, logList } = toRefs(state);
</script>
<style lang="scss" scoped>
.detail_container {
width: 100%;
height: calc(100vh - 56px);
padding: 0 24px;
min-height: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
.flex_row {
// flex-grow: 1;
width: 100%;
display: flex;
margin-bottom: 16px;
position: relative;
height: calc(100% - 62px);
.flex_left {
width: 240px;
background-color: #fff;
margin-right: 16px;
border-radius: 6px;
padding: 16px;
height: 100%;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
overflow: hidden;
.box {
width: 208px;
height: 100%;
overflow: hidden;
.type_station {
width: 100%;
margin-top: 16px;
height: calc(100% - 50px);
.type-box {
overflow: hidden;
text-overflow: ellipsis;
height: 32px;
white-space: nowrap;
line-height: 32px;
text-align: center;
font-size: 14px;
color: #404a62;
cursor: pointer;
&:hover {
background-color: #f2f3f7;
}
}
.current-type {
background-color: #f2f3f7;
border-radius: 4px;
}
}
}
}
.flex_right {
flex: 1;
height: 100%;
.form-filter {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
height: 68px;
width: 100%;
padding: 16px 16px 16px 24px;
background-color: #ffffff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
.el-form {
width: calc(100% - 144px);
}
.el-form-item {
margin-bottom: 0px;
}
.operate_btns {
width: 144px;
}
}
.search-result {
width: 100%;
height: calc(100% - 84px);
padding: 15px 0;
color: #fff;
background-color: #1a1a1a;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.1);
border-radius: 6px;
.item-container {
overflow: auto;
padding: 0 15px;
height: 100%;
}
p {
word-wrap: break-word;
word-break: break-all;
}
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入账号、手机号和所属组织">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="exportFile">
<bg-icon
style="font-size: 12px; color: #fff; margin-right: 8px"
icon="#bg-ic-file-send"></bg-icon>
导出
</el-button>
<el-popconfirm
class="force-modify-btn"
width="220"
icon-color="#ff9900"
title="确认修改密码吗?"
@confirm="forceModifyPwd">
<template #reference>
<el-button slot="reference" type="primary">
<span>强制修改密码</span>
</el-button>
</template>
</el-popconfirm>
<span class="sleceted_tip"
>已选<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">用户类型</span>
<el-select v-model="filter.is_admin" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in userTypeList"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">上次访问时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px" />
</div>
<div class="filter_item">
<span class="filter_title">活跃度</span>
<el-select v-model="filter.active" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in activeOptions"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">密码强度</span>
<el-select v-model="filter.pwd_level" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in pwdLevelList"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<bg-table
ref="accountTable"
:headers="headers"
:rows="tableRows"
:stripe="true"
select
@selectAc="selectChange">
<template v-slot:active="{ row }">
<span
class="active"
:class="{
active_weak: row.active === '低',
active_middle: row.active === '中',
active_heigh: row.active === '高',
}"
>{{ row.active }}</span
>
</template>
<template v-slot:pwd_level="{ row }">
<div class="pwd_level-sign">
<span
:class="{
pwd_level: true,
pwd_level_weak: row.pwd_level === '弱',
pwd_level_middle: row.pwd_level === '中',
pwd_level_heigh: row.pwd_level === '强',
}">
{{ row.pwd_level === "" ? row.pwd_level : "" }}</span
>
<span
:class="{
pwd_level: true,
pwd_level_middle: row.pwd_level === '中',
pwd_level_heigh: row.pwd_level === '强',
}"
>{{ row.pwd_level === "" ? row.pwd_level : "" }}</span
>
<span
:class="{
pwd_level: true,
pwd_level_heigh: row.pwd_level === '强',
}"
>{{ row.pwd_level === "" ? row.pwd_level : "" }}</span
>
</div>
</template>
</bg-table>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="filter.total"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, ref, computed, onBeforeMount } from "vue";
import { useStore } from "vuex";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { downloadBlob } from "@/services/helper.js";
const { getters } = useStore();
const userTypeList = computed(() => {
const temp = getters.getUserTypeConfig || {};
return Object.keys(temp).map((key) => ({
label: temp[key],
value: +key,
}));
});
const accountTable = ref(null);
const state = reactive({
filter: {
search: "",
is_admin: "",
time: [],
active: 0,
pwd_level: "",
page: 1,
limit: 10,
total: 0,
},
activeOptions: [
{ label: "全部", value: "" },
{ label: "", value: 1 },
{ label: "", value: 2 },
{ label: "", value: 3 },
],
pwdLevelList: [
{ label: "全部", value: "" },
{ label: "", value: 1 },
{ label: "", value: 2 },
{ label: "", value: 3 },
],
headers: [
{
label: "账号",
prop: "system_account",
// minWidth: 280,
},
{
label: "手机号",
prop: "contact_phone",
// minWidth: 360,
},
{
label: "用户类型",
prop: "is_admin",
// width: 200,
},
{
label: "所属组织",
prop: "name",
// width: 120,
},
{
label: "活跃度",
prop: "active",
// width: 120,
},
{
label: "密码强度",
prop: "pwd_level",
width: 240,
},
{
label: "密码使用时长(天)",
prop: "pwd_is_used",
width: 240,
},
{
label: "创建时间",
prop: "created_time",
width: 240,
},
],
tableRows: [],
//选中数量
selectedNum: 0,
actionRow: {},
dialogDetail: false,
cacheDialog: false,
});
const { filter, activeOptions, pwdLevelList, headers, tableRows, selectedNum } = toRefs(state);
//选择项发生变化
const selectChange = (params) => {
state.selectedNum = params.allLength;
};
//导出
const exportFile = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
axios
.get(`/apaas/system/v5/log/userAccountAudit/list/export`, {
params,
responseType: "blob",
})
.then((res) => {
if (res.status === 200) {
const type =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
downloadBlob(res.data, type);
} else {
ElMessage.error(res.statusText);
}
});
};
//强制修改密码
const forceModifyPwd = () => {
const selectedObj = accountTable.value.dealSelectData();
const selectedRows = Object.values(selectedObj);
if (!selectedRows.length) return ElMessage.warning("请先选择修改项");
const ids = selectedRows.map((item) => item.id);
axios.post("/apaas/system/v5/user/resetpwd", { ids }).then((res) => {
if (res.data.code === 200) {
ElMessage.success(res.data.msg);
cleanSelected();
getTableRows();
} else {
ElMessage.error(res.data.data);
}
});
};
//清空
const cleanSelected = () => {
accountTable.value.clearTable();
};
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
};
const changePage = (page) => {
state.filter.page = page;
getTableRows();
};
const getTableRows = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
axios
.get(`/apaas/system/v5/log/userAccountAudit/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.filter.total = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
const filterAction = () => {
changePage(1);
};
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
};
const filterClear = () => {
state.filter = {
search: "",
is_admin: "",
time: [],
active: 0,
pwd_level: "",
page: 1,
limit: 10,
total: 0,
};
changePage(1);
};
onBeforeMount(() => {
getTableRows();
});
</script>
<style lang="scss" scoped>
.page_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
padding: 0 24px;
min-height: 100%;
.main_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex-grow: 1;
width: 100%;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
padding: 0 0 16px 0;
margin-bottom: 16px;
position: relative;
.sleceted_tip {
margin: 0 24px 0 40px;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
.filter_item {
display: flex;
align-items: center;
.filter_title {
width: 84px;
white-space: pre-wrap;
font-weight: normal;
}
}
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
padding: 0 16px;
.el-table {
flex: 1;
:deep() td {
padding: 9px 0 !important;
}
.active {
display: inline-block;
width: 48px;
height: 24px;
text-align: center;
line-height: 24px;
font-size: 12px;
border-radius: 3px;
&.active_weak {
color: #dd6955;
background-color: #fcf0ee;
border: solid 1px #eba599;
}
&.active_middle {
color: #ea7d19;
background-color: #fffae8;
border: solid 1px #fac066;
}
&.active_heigh {
color: #429e8a;
background-color: #f1f9f7;
border: solid 1px #9ad5c8;
}
}
.pwd_level-sign {
display: flex;
align-items: center;
}
.pwd_level {
display: inline-block;
width: 40px;
height: 14px;
line-height: 14px;
color: #fff;
text-align: center;
font-size: 12px;
background-color: #dadee7;
border-radius: 3px;
&:not(:first-child) {
margin-left: 4px;
}
&.pwd_level_weak {
background-color: #dd6955;
}
&.pwd_level_middle {
background-color: #ea7d19;
}
&.pwd_level_heigh {
background-color: #429e8a;
}
}
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="page_content">
<bg-form-gap title="详情"></bg-form-gap>
<bg-detail-table2 class="detail_info" :list="instanceData.detailInfo">
<template #req_url_value="{ data }">
<el-popover placement="top-start" :width="600" trigger="hover" :content="data.value">
<template #reference>
<p class="show_in_line">{{ data.value }}</p>
</template>
</el-popover>
</template>
<template #res_fields_value="{ data }">
<el-popover placement="bottom-start" :width="800" trigger="hover" :content="data.value">
<template #reference>
<p class="show_in_line">{{ data.value }}</p>
</template>
</el-popover>
</template>
</bg-detail-table2>
</div>
</div>
</template>
<script setup>
import { reactive, onBeforeMount } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import { useRoute } from "vue-router";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const route = useRoute();
const instanceData = reactive({
detailInfo: [
{
key: "system_account",
label: "账号",
value: "",
// slot: "approvalStatus",
},
{
key: "phone",
label: "手机号",
value: "",
},
{
key: "user_type",
label: "用户类型",
value: "",
},
{
key: "org_name",
label: "所属组织",
value: "",
},
{
key: "system_module",
label: "系统模块",
value: "",
},
{
key: "created_time",
label: "操作时间",
value: "",
},
{
key: "operate_type",
label: "操作类型",
value: "",
},
{
key: "req_method_str",
label: "请求方式",
value: "",
},
{
key: "operate_status_str",
label: "操作状态",
value: "",
},
{
key: "operate_ip",
label: "操作地址",
value: "",
},
{
key: "operate_addr",
label: "操作地点",
value: "",
width: "100%",
},
{
key: "req_url",
label: "请求地址",
value: "",
width: "100%",
childSlot: "req_url_value",
},
{
key: "req_param",
label: "请求参数",
value: "",
},
{
key: "operate_method",
label: "操作方法",
value: "",
},
{
key: "res_fields",
label: "返回参数",
value: "",
childSlot: "res_fields_value",
},
],
});
onBeforeMount(() => {
getDetailInfo();
});
const getDetailInfo = () => {
axios
.get(`/apaas/system/v5/log/userBehavior/detail/${route.query.id}`)
.then((res) => {
if (res.data.code == 200) {
const result = res.data.data || {};
instanceData.detailInfo.forEach((item) => {
item.value = result[item.key];
});
} else {
ElMessage.error(res.data.data);
}
})
.catch((err) => {
ElMessage.error(err);
});
};
</script>
<style lang="scss" scoped>
.page_content {
position: relative;
padding: 24px 0 68px 24px;
.detail_info,
.approve_info {
margin-bottom: 24px;
width: 960px;
}
.show_in_line {
overflow: hidden;
width: 824px;
text-overflow: ellipsis;
white-space: nowrap;
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入账号、手机号和所属组织">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="exportFile">
<bg-icon
style="font-size: 12px; color: #fff; margin-right: 8px"
icon="#bg-ic-file-send"></bg-icon>
导出
</el-button>
<el-button @click="deleteBatch"> 批量删除 </el-button>
<span class="sleceted_tip"
>已选<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">用户类型</span>
<el-select v-model="filter.is_admin" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in userTypeList"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">操作时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px" />
</div>
<div class="filter_item">
<span class="filter_title">操作类型</span>
<el-select v-model="filter.operate_type" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in operateTypeList"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">请求方式</span>
<el-select v-model="filter.req_method" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in reqMethodList"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">操作状态</span>
<el-select v-model="filter.operate_status" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in operateStatusList"
:key="index"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<bg-table
ref="userTable"
:headers="headers"
:rows="tableRows"
:stripe="true"
select
@selectAc="selectChange">
<template v-slot:is_admin="{ row }">
<span>{{ userTypeConfig[row.is_admin] }}</span>
</template>
<template v-slot:operate_type="{ row }">
<span>{{ filterOperateType(row.operate_type) }}</span>
</template>
<template v-slot:req_method="{ row }"
><span>{{ filterReqMethod(row.req_method) }}</span></template
>
<template v-slot:operate_status="{ row }"
><span class="operate_status" :class="{ fail: row.operate_status === 2 }">{{
row.operate_status === 1 ? "成功" : "失败"
}}</span></template
>
<template v-slot:action="{ row }">
<bg-table-btns2 :limit="3" :key="row.id">
<bg-table-btn @click="goDetail(row)">详情</bg-table-btn>
<bg-table-btn @click="deleteCurrent(row)">删除</bg-table-btn>
</bg-table-btns2>
</template>
</bg-table>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="filter.total"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
<el-dialog class="dialog_box" title="提示" v-model="deleteDialog" width="400px">
<div style="font-size: 16px; color: #404a62">{{ deleteTips }}</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="deleteDialog = false">取 消</el-button>
<el-button type="primary" @click="confirmDelete">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, toRefs, computed, onBeforeMount } from "vue";
import { useStore } from "vuex";
import { useRouter } from "vue-router";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { operateTypeList, reqMethodList } from "@/services/enumList.js";
import { downloadBlob } from "@/services/helper.js";
const { getters } = useStore();
const router = useRouter();
const userTable = ref(null);
const userTypeConfig = computed(() => getters.getUserTypeConfig || {});
const userTypeList = computed(() => {
const temp = getters.getUserTypeConfig || {};
return Object.keys(temp).map((key) => ({
label: temp[key],
value: +key,
}));
});
const state = reactive({
filter: {
search: "",
is_admin: "",
time: [],
operate_type: "",
req_method: "",
operate_status: "",
page: 1,
limit: 10,
total: 0,
},
operateStatusList: [
{ label: "全部", value: "" },
{ label: "成功", value: 1 },
{ label: "失败", value: 2 },
],
headers: [
{
label: "账号",
prop: "system_account",
// minWidth: 280,
},
{
label: "用户类型",
prop: "is_admin",
// minWidth: 360,
},
{
label: "系统模块",
prop: "system_module",
// width: 200,
},
{
label: "操作类型",
prop: "operate_type",
// width: 120,
},
{
label: "请求方式",
prop: "req_method",
// width: 120,
},
{
label: "操作状态",
prop: "operate_status",
// width: 240,
},
{
label: "操作地点",
prop: "operate_addr",
// width: 240,
},
{
label: "操作时间",
prop: "created_time",
// width: 240,
},
{
label: "操作",
prop: "action",
width: 120,
},
],
//选中数量
selectedNum: 0,
detailHeaders: [
{
label: "账号",
prop: "ability_name",
// minWidth: 280,
},
{
label: "类型",
prop: "synopsis",
// minWidth: 360,
},
{
label: "用户手机号",
prop: "created_time",
// width: 200,
},
{
label: "所属组织",
prop: "ability_state",
// width: 120,
},
],
tableRows: [],
tableTotal: 0,
//选中项
selectedRows: [],
actionRow: {},
dialogDetail: false,
deleteDialog: false,
deleteTips: "",
});
const { filter, operateStatusList, headers, selectedNum, tableRows, deleteDialog, deleteTips } =
toRefs(state);
//导出
const exportFile = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
delete params.total;
axios
.get(`/apaas/system/v5/log/userBehavior/list/export`, {
params,
responseType: "blob",
})
.then((res) => {
if (res.status === 200) {
const type =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";
downloadBlob(res.data, type);
} else {
ElMessage.error(res.statusText);
}
});
};
//请求方式过滤器
const filterReqMethod = (value) => {
const findItem = reqMethodList.find((item) => item.value === value);
return (findItem || {}).label;
};
//操作类型过滤器
const filterOperateType = (value) => {
const findItem = operateTypeList.find((item) => item.value === value);
return (findItem || {}).label;
};
//选择项发生变化
const selectChange = (params) => {
state.selectedNum = params.allLength;
};
//批量删除
const deleteBatch = () => {
state.selectedRows = [];
const selectedObj = userTable.value.dealSelectData();
state.selectedRows = Object.values(selectedObj);
if (!state.selectedRows.length) return ElMessage.warning("请先选择删除项");
state.deleteTips = "确定要删除选中项吗?";
state.deleteDialog = true;
};
//删除当前行
const deleteCurrent = (row) => {
state.selectedRows = [row];
state.deleteTips = "确定要删除当前行吗?";
state.deleteDialog = true;
};
//确定删除
const confirmDelete = () => {
const ids = state.selectedRows.map((item) => item.id);
axios
.delete(`/apaas/system/v5/log/userBehavior/delete?ids=${ids.join(",")}`)
.then((res) => {
if (res.data.code === 200) {
ElMessage.success(res.data.data);
cleanSelected();
getTableRows();
} else {
ElMessage.error(res.data.data);
}
})
.finally(() => {
state.deleteDialog = false;
});
};
//清空
const cleanSelected = () => {
userTable.value.clearTable();
};
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
};
const changePage = (page) => {
state.filter.page = page;
getTableRows();
};
const getTableRows = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
axios
.get(`/apaas/system/v5/log/userBehavior/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.filter.total = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
const filterAction = () => {
changePage(1);
};
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
};
const filterClear = () => {
state.filter = {
search: "",
is_admin: "",
time: [],
operate_type: "",
req_method: "",
operate_status: "",
page: 1,
limit: 10,
total: 0,
};
changePage(1);
};
const goDetail = (row) => {
router.push({
path: "/log/userBehavior/detail",
query: {
id: row.id,
},
});
};
onBeforeMount(() => {
getTableRows();
});
</script>
<style lang="scss" scoped>
.page_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
padding: 0 24px;
min-height: 100%;
.main_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex-grow: 1;
width: 100%;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
padding: 0 0 16px 0;
margin-bottom: 16px;
position: relative;
.sleceted_tip {
margin: 0 24px 0 40px;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
.filter_item {
display: flex;
align-items: center;
.filter_title {
width: 84px;
white-space: pre-wrap;
font-weight: normal;
}
}
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
padding: 0 16px;
.el-table {
flex: 1;
:deep() td {
padding: 9px 0 !important;
}
}
.operate_status {
&::before {
display: inline-block;
content: "";
width: 6px;
height: 6px;
margin-right: 6px;
vertical-align: middle;
background-color: #48ad97;
border-radius: 50%;
}
&.fail::before {
background-color: #dd6955;
}
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入问题标题、问题内容、回答内容、回复人">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="openDeleteTipDialog(2)"> 批量删除 </el-button>
<span class="sleceted_tip"
>已选<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">问题对象</span>
<el-select v-model="filter.question_obj" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in questionObjOption"
:key="'questionObj' + index"
:label="item.name"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">回复时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px" />
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<bg-table
ref="rulesTable"
:headers="headers"
:rows="tableRows"
:stripe="true"
select
@selectAc="selectRows">
<template v-slot:title="{ row }">
<a
class="can_click_text"
:href="`/apaas/knowledge/ui/#/qa-center/detail?id=${row.id}`"
target="_blank"
>{{ row.title }}</a
>
</template>
<template v-slot:question_obj="{ row }">
<span>{{ questionObjArr[row.question_obj] }}</span>
</template>
<template v-slot:action="{ row }">
<bg-table-btn @click="openDeleteTipDialog(1, row)">删除</bg-table-btn>
</template>
</bg-table>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
<el-dialog class="dialog_box" title="提示" v-model="dialogDeleteTips" width="400px">
<div class="dialog-alert">
<bg-icon
class=""
icon="#bg-ic-s-circle-warning" />删除后,提问及答复内容均被删除且不可恢复, 确定删除吗?
</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDeleteTips = false">取 消</el-button>
<el-button type="primary" @click="openDeleteDialog">确 定</el-button>
</div>
</template>
</el-dialog>
<el-dialog
class="dialog_box"
:title="deleteDialogConfig.title"
v-model="dialogDelete"
width="400px">
<!-- <div style="text-align: center">
{{ deleteDialogConfig.tips }}
</div> -->
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="80px">
<el-form-item label="删除理由" prop="delete_reason">
<el-input
v-model.trim="ruleForm.delete_reason"
type="textarea"
:rows="4"
placeholder="请输入至少5个字符,至多50个字符" />
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="confimDelete">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, onBeforeMount, ref } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const ruleFormRef = ref();
const rulesTable = ref();
const state = reactive({
filter: {
search: "",
question_obj: "0",
time: [],
page: 1,
limit: 10,
},
headers: [
{
label: "回答内容",
prop: "answer_content",
},
{
label: "问题标题",
prop: "title",
},
{
label: "问题内容",
prop: "content",
},
{
label: "问题对象",
prop: "question_obj",
width: 100,
},
{
label: "答复数",
prop: "comments",
width: 70,
},
{
label: "回复人",
prop: "answer_user",
},
{
label: "回复时间",
prop: "created_time",
width: 200,
},
{
label: "操作",
prop: "action",
width: 80,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
selectedNum: 0,
actionRow: {},
selection: [],
dialogDeleteTips: false,
dialogDelete: false,
deleteDialogConfig: {
title: "删除",
idList: [],
tips: "确定要删除当前行数据吗?",
},
questionObjOption: [
{
name: "全部",
value: "0",
},
{
name: "能力",
value: "1",
},
{
name: "应用",
value: "2",
},
{
name: "平台",
value: "3",
},
],
ruleForm: {
delete_reason: "",
},
rules: {
delete_reason: [
{
required: true,
message: "请输入删除理由",
trigger: "blur",
},
{
min: 5,
message: "最少5个字符",
trigger: "blur",
},
{
max: 50,
message: "最多50个字符",
trigger: "blur",
},
],
},
});
const questionObjArr = state.questionObjOption.map((item) => item.name);
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 关键字
const changePage = (page) => {
state.filter.page = page;
getTableRows();
}; // 改变页码
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
}; // 改变每页条数
const filterAction = () => {
changePage(1);
}; // 高级搜索中查询
const filterClear = () => {
state.filter = {
search: "",
page: 1,
limit: 10,
time: [],
};
changePage(1);
}; // 高级搜索中重置
const getTableRows = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
axios
.get(`/apaas/knowledge/v5/question/manage/answers/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取表格数据
const selectRows = (data) => {
state.selectedNum = data.allLength;
state.selection = data.selection;
};
const cleanSelected = () => {
rulesTable.value.clearTable();
}; //清空
const openDeleteTipDialog = (type, row) => {
state.dialogDeleteTips = true;
state.actionRow = row;
state.deleteType = type;
};
//打开删除弹窗
const openDeleteDialog = () => {
if (state.deleteType === 2) {
const res = rulesTable.value.dealSelectData();
const list = Object.values(res);
if (list.length === 0) return ElMessage.warning("请选择要删除的数据");
state.dialogDelete = true;
const idList = list.map((item) => item.id);
state.deleteDialogConfig = { title: "操作提示", idList, tips: "确定要删除所选数据吗?" };
return;
}
const idList = [state.actionRow.id];
state.dialogDelete = true;
state.deleteDialogConfig = {
title: "删除",
idList: idList,
tips: "确定要删除当前行数据吗?",
};
};
//确定删除
const confimDelete = () => {
ruleFormRef.value.validate((valid) => {
if (valid) {
const data = {
ids: state.deleteDialogConfig.idList.join(","),
delete_reason: state.ruleForm.delete_reason,
};
axios
.delete("/apaas/knowledge/v5/question/manage/answer", {
data,
})
.then((res) => {
if (res.data.code == 200) {
ElMessage.success("删除成功");
changePage(1);
} else {
ElMessage.error(res.data.data);
}
})
.finally(() => {
ruleFormRef.value.resetFields();
state.dialogDeleteTips = false;
state.dialogDelete = false;
});
}
});
return;
};
const {
filter,
headers,
tableRows,
tableTotal,
selectedNum,
questionObjOption,
dialogDeleteTips,
dialogDelete,
deleteDialogConfig,
ruleForm,
rules,
} = toRefs(state);
onBeforeMount(() => {
getTableRows();
});
</script>
<style lang="scss" scoped>
:deep(.bg-filter-group) .top-filter .right-filter {
width: 520px;
.el-input {
width: 360px;
}
}
.page_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
padding: 0 24px;
min-height: 100%;
.main_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex-grow: 1;
width: 100%;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
padding: 0 0 16px 0;
margin-bottom: 16px;
position: relative;
.sleceted_tip {
margin: 0 24px 0 40px;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
.filter_item {
display: flex;
align-items: center;
.filter_title {
width: 84px;
white-space: pre-wrap;
font-weight: normal;
}
}
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
padding: 0 16px;
.el-table {
flex: 1;
.under_line {
text-decoration: underline;
}
}
}
}
:deep() .dialog_box_detail {
.el-dialog__body {
padding: 0 0 18px 0;
height: 580px;
}
.form_filter {
display: flex;
justify-content: space-between;
align-items: center;
height: 69px;
border-bottom: 1px solid #e6e9ef;
.right {
margin-right: 16px;
.el-button {
width: 64px;
&:last-child {
margin-left: 16px;
}
}
}
}
.table_content {
padding: 0 16px;
}
}
}
.dialog-alert {
text-align: left;
line-height: 24px;
.bg-icon {
margin-right: 8px;
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入问题标题、问题内容、发布人">
<template v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="openDeleteTipDialog(2)"> 批量删除 </el-button>
<span class="sleceted_tip"
>已选<span class="num">{{ selectedNum }}</span
></span
>
<span class="clean" @click="cleanSelected">清空</span>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">问题对象</span>
<el-select v-model="filter.question_obj" placeholder="请选择" style="width: 300px">
<el-option
v-for="(item, index) in questionObjOption"
:key="'questionObj' + index"
:label="item.name"
:value="item.value">
</el-option>
</el-select>
</div>
<div class="filter_item">
<span class="filter_title">发布时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px" />
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container">
<bg-table
ref="rulesTable"
:headers="headers"
:rows="tableRows"
:stripe="true"
select
@selectAc="selectRows">
<template v-slot:title="{ row }">
<a
class="can_click_text"
:href="`/apaas/knowledge/ui/#/qa-center/detail?id=${row.id}`"
target="_blank"
>{{ row.title }}</a
>
</template>
<template v-slot:question_obj="{ row }">
<span>{{ questionObjArr[row.question_obj] }}</span>
</template>
<template v-slot:action="{ row }">
<bg-table-btn @click="openDeleteTipDialog(1, row)">删除</bg-table-btn>
</template>
</bg-table>
<bg-pagination
:page="filter.page"
:size="filter.limit"
:total="tableTotal"
@change-page="changePage"
@change-size="changeSize">
</bg-pagination>
</div>
</div>
<el-dialog class="dialog_box" title="提示" v-model="dialogDeleteTips" width="400px">
<div class="dialog-alert">
<bg-icon
class=""
icon="#bg-ic-s-circle-warning" />删除后,提问及答复内容均被删除且不可恢复, 确定删除吗?
</div>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDeleteTips = false">取 消</el-button>
<el-button type="primary" @click="openDeleteDialog">确 定</el-button>
</div>
</template>
</el-dialog>
<el-dialog
class="dialog_box"
:title="deleteDialogConfig.title"
v-model="dialogDelete"
width="400px">
<!-- <div style="text-align: center">
{{ deleteDialogConfig.tips }}
</div> -->
<el-form ref="ruleFormRef" :model="ruleForm" :rules="rules" label-width="80px">
<el-form-item label="删除理由" prop="delete_reason">
<el-input
v-model.trim="ruleForm.delete_reason"
type="textarea"
:rows="4"
placeholder="请输入至少5个字符,至多50个字符" />
</el-form-item>
</el-form>
<template v-slot:footer>
<div class="apaas_button">
<el-button type="default" @click="dialogDelete = false">取 消</el-button>
<el-button type="primary" @click="confimDelete">确 定</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, toRefs, onBeforeMount, ref } from "vue";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
const ruleFormRef = ref();
const rulesTable = ref();
const state = reactive({
filter: {
search: "",
question_obj: "0",
time: [],
page: 1,
limit: 10,
},
headers: [
{
label: "问题标题",
prop: "title",
},
{
label: "问题内容",
prop: "content",
},
{
label: "问题对象",
prop: "question_obj",
width: 100,
},
{
label: "答复数",
prop: "comments",
width: 70,
},
{
label: "发布人",
prop: "created_by",
},
{
label: "发布时间",
prop: "created_time",
width: 200,
},
{
label: "操作",
prop: "action",
width: 80,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
selectedNum: 0,
actionRow: {},
selection: [],
dialogDeleteTips: false,
dialogDelete: false,
deleteDialogConfig: {
title: "删除",
idList: [],
tips: "确定要删除当前行数据吗?",
},
questionObjOption: [
{
name: "全部",
value: "0",
},
{
name: "能力",
value: "1",
},
{
name: "应用",
value: "2",
},
{
name: "平台",
value: "3",
},
],
ruleForm: {
delete_reason: "",
},
rules: {
delete_reason: [
{
required: true,
message: "请输入删除理由",
trigger: "blur",
},
{
min: 5,
message: "最少5个字符",
trigger: "blur",
},
{
max: 50,
message: "最多50个字符",
trigger: "blur",
},
],
},
});
const questionObjArr = state.questionObjOption.map((item) => item.name);
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 关键字
const changePage = (page) => {
state.filter.page = page;
getTableRows();
}; // 改变页码
const changeSize = (size) => {
state.filter.limit = size;
changePage(1);
}; // 改变每页条数
const filterAction = () => {
changePage(1);
}; // 高级搜索中查询
const filterClear = () => {
state.filter = {
search: "",
page: 1,
limit: 10,
time: [],
};
changePage(1);
}; // 高级搜索中重置
const getTableRows = () => {
let params = { ...state.filter };
params.start_at = params.time ? params.time[0] || "" : "";
params.end_at = params.time ? params.time[1] || "" : "";
delete params.time;
axios
.get(`/apaas/knowledge/v5/question/manage/ask/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
}; // 获取表格数据
const selectRows = (data) => {
state.selectedNum = data.allLength;
state.selection = data.selection;
};
const cleanSelected = () => {
rulesTable.value.clearTable();
}; //清空
const openDeleteTipDialog = (type, row) => {
state.dialogDeleteTips = true;
state.actionRow = row;
state.deleteType = type;
};
//打开删除弹窗
const openDeleteDialog = () => {
if (state.deleteType === 2) {
const res = rulesTable.value.dealSelectData();
const list = Object.values(res);
if (list.length === 0) return ElMessage.warning("请选择要删除的数据");
state.dialogDelete = true;
const idList = list.map((item) => item.id);
state.deleteDialogConfig = { title: "操作提示", idList, tips: "确定要删除所选数据吗?" };
return;
}
const idList = [state.actionRow.id];
state.dialogDelete = true;
state.deleteDialogConfig = {
title: "删除",
idList: idList,
tips: "确定要删除当前行数据吗?",
};
};
//确定删除
const confimDelete = () => {
ruleFormRef.value.validate((valid) => {
if (valid) {
const data = {
ids: state.deleteDialogConfig.idList.join(","),
delete_reason: state.ruleForm.delete_reason,
};
axios
.delete("/apaas/knowledge/v5/question/manage/ask", {
data,
})
.then((res) => {
if (res.data.code == 200) {
ElMessage.success("删除成功");
changePage(1);
} else {
ElMessage.error(res.data.data);
}
})
.finally(() => {
ruleFormRef.value.resetFields();
state.dialogDeleteTips = false;
state.dialogDelete = false;
});
}
});
return;
};
const {
filter,
headers,
tableRows,
tableTotal,
selectedNum,
questionObjOption,
dialogDeleteTips,
dialogDelete,
deleteDialogConfig,
ruleForm,
rules,
} = toRefs(state);
onBeforeMount(() => {
getTableRows();
});
</script>
<style lang="scss" scoped>
:deep(.bg-filter-group) .top-filter .right-filter {
width: 520px;
.el-input {
width: 360px;
}
}
.page_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
width: 100%;
padding: 0 24px;
min-height: 100%;
.main_container {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
flex-grow: 1;
width: 100%;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
padding: 0 0 16px 0;
margin-bottom: 16px;
position: relative;
.sleceted_tip {
margin: 0 24px 0 40px;
vertical-align: middle;
font-size: 14px;
color: #404a62;
.num {
margin: 0 3px 0 3px;
font-weight: 600;
color: #202531;
}
}
.clean {
vertical-align: middle;
font-size: 14px;
color: #3759be;
cursor: pointer;
}
.filter-group {
.left-filter {
flex: 1;
display: flex;
justify-content: start;
flex-wrap: wrap;
.filter_item {
display: flex;
align-items: center;
.filter_title {
width: 84px;
white-space: pre-wrap;
font-weight: normal;
}
}
}
.right-action {
width: 144px;
padding-bottom: 16px;
.el-button {
width: 64px;
}
}
}
.table_container {
padding: 0 16px;
.el-table {
flex: 1;
.under_line {
text-decoration: underline;
}
}
}
}
:deep() .dialog_box_detail {
.el-dialog__body {
padding: 0 0 18px 0;
height: 580px;
}
.form_filter {
display: flex;
justify-content: space-between;
align-items: center;
height: 69px;
border-bottom: 1px solid #e6e9ef;
.right {
margin-right: 16px;
.el-button {
width: 64px;
&:last-child {
margin-left: 16px;
}
}
}
}
.table_content {
padding: 0 16px;
}
}
}
.dialog-alert {
text-align: left;
line-height: 24px;
.bg-icon {
margin-right: 8px;
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="flex_row">
<div class="flex_left bgc_white">
<div class="tree_header">文档类型</div>
<div class="tree_content">
<div class="search">
<el-input v-model="filterTree" placeholder="请输入文档类型名称" :prefix-icon="Search" />
</div>
<div class="tree">
<el-tree
ref="treeRef"
:data="typeData"
:props="defaultProps"
@node-click="handleNodeClick"
node-key="classify_id"
:highlight-current="true"
:filter-node-method="filterNode"
:default-expand-all="true">
<template #default="{ node, data }">
<div class="custom_tree_node">
<span class="label-text" :title="node.label">{{ node.label }} </span>
</div>
</template>
</el-tree>
</div>
</div>
</div>
<div class="flex_right bgc_white">
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入文档类型名称">
<template v-if="treeLevel === 0" v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="addDocType">
<bg-icon
style="font-size: 12px; color: #fff; margin-right: 8px"
icon="#bg-ic-add"></bg-icon>
新增
</el-button>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">状态</span>
<el-select v-model="filter.is_active" placeholder="请选择" style="width: 300px">
<el-option label="全部" value=""></el-option>
<el-option label="已启用" :value="1"> </el-option>
<el-option label="已停用" :value="0"> </el-option>
</el-select>
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container apaas_scroll">
<bg-table
ref="dataTable"
:headers="headers"
:rows="tableRows"
:isIndex="true"
:stripe="true">
<template v-slot:is_active="{ row }">
<el-switch
style="
--el-switch-on-color: #2b4695;
--el-switch-off-color: #cbced7;
height: 20px;
"
v-model="row.is_active"
inline-prompt
:active-value="1"
:inactive-value="0"
active-text="是"
inactive-text="否"
:before-change="() => beforeSwitchStatus(row)" />
</template>
<template v-slot:action="{ row }">
<bg-table-btn @click="editCurType(row)"> 编辑 </bg-table-btn>
<bg-table-btn @click="deleteCurType(row)"> 删除 </bg-table-btn>
</template>
</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>
<el-dialog
v-model="secondTipsDialogConfig.visible"
:title="secondTipsDialogConfig.title"
width="520px">
<div class="warning_info">
<bg-icon
style="font-size: 12px; color: #a9b1c7; margin-right: 8px; vertical-align: baseline"
icon="#bg-ic-circle-tips"></bg-icon>
{{ secondTipsDialogConfig.content }}
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseSecondTipsDialog">取消</el-button>
<el-button type="primary" @click="secondConfirm(secondTipsDialogConfig.type)"
>确定</el-button
>
</span>
</template>
</el-dialog>
<el-dialog
v-model="formDialogConfig.visible"
:title="formDialogConfig.title"
width="600px"
:before-close="handleCloseForm">
<el-form label-width="80px" :model="formData" :rules="rules" ref="ruleForm">
<el-form-item required label="上级类型" prop="parent_id">
<el-tree-select
v-model="formData.parent_id"
:data="parentTypeData"
:props="defaultProps"
node-key="classify_id"
:highlight-current="true"
:filter-node-method="filterNode"
check-strictly
filterable
:render-after-expand="false" />
</el-form-item>
<el-form-item label="类型名称" prop="classify_name">
<el-input v-model="formData.classify_name" placeholder="请输入"> </el-input>
</el-form-item>
<el-form-item label="排序" prop="sort">
<el-input v-model.number.trim="formData.sort" placeholder="请输入"> </el-input>
</el-form-item>
<el-form-item label="备注" prop="describe">
<el-input v-model="formData.describe" :rows="3" type="textarea" placeholder="请输入">
</el-input>
</el-form-item>
<el-form-item label="是否启用" prop="is_active">
<el-switch
style="--el-switch-on-color: #2b4695; --el-switch-off-color: #cbced7; height: 20px"
v-model="formData.is_active"
inline-prompt
:active-value="1"
:inactive-value="0"
active-text="是"
inactive-text="否"
:before-change="() => beforeSwitchStatusInEdit()" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseForm">取消</el-button>
<el-button type="primary" @click="confirmForm">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { watch, nextTick, reactive, toRefs, ref, computed, onBeforeMount } from "vue";
import { useRouter } from "vue-router";
import axios from "@/request/http.js";
import { topId } from "../helper.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const dataTable = ref(null);
const ruleForm = ref(null);
const selectNode = ref(null);
const selectNodeObj = ref(null);
const filterTree = ref("");
const treeRef = ref(null);
//校验上级类型
const state = reactive({
filter: {
search: "",
is_active: "",
page: 1,
limit: 10,
},
headers: [
{
label: "类型名称",
prop: "classify_name",
minWidth: 160,
},
{
label: "上级类型名称",
prop: "parent_classify_name",
minWidth: 160,
},
{
label: "启用状态",
prop: "is_active",
},
{
label: "操作",
prop: "action",
width: 220,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
formDialogConfig: {
visible: false,
title: "新增",
type: "add",
},
formData: {
parent_id: "",
classify_name: "",
sort: "",
describe: "",
is_active: 0,
},
rules: {
parent_id: [
{
required: true,
message: "请选择",
trigger: "change",
},
],
classify_name: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
},
//左侧类型树层级
treeLevel: 0,
//删除确认弹框
secondTipsDialogConfig: {
visible: false,
title: "",
type: "",
content: "",
},
selectedRow: null,
});
const {
filter,
headers,
tableRows,
tableTotal,
formDialogConfig,
formData,
rules,
treeLevel,
secondTipsDialogConfig,
} = toRefs(state);
onBeforeMount(() => {
getTypeTree();
});
watch(filterTree, (val) => {
treeRef.value.filter(val);
});
watch(selectNode, (val) => {
state.formData.parent_id = val;
});
const filterAction = () => {
changePage(1);
};
const filterClear = () => {
state.filter = {
search: "",
is_active: "",
page: 1,
limit: 10,
};
changePage(1);
};
const getTableRows = () => {
let params = {
...state.filter,
classify_id: selectNodeObj.value.id ? selectNodeObj.value.id : undefined,
};
axios
.get(`/apaas/knowledge/v5/documentmgr/classify/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = (res.data.data || []).map((item) => ({
...item,
parent_classify_name: item.parent_id === topId ? "平台公开" : item.parent_classify_name,
}));
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
//新增文档类型
const addDocType = () => {
state.formDialogConfig = {
visible: true,
title: "新增",
type: "add",
};
};
const handleNodeClick = (data) => {
selectNode.value = data.classify_id;
selectNodeObj.value = data;
state.treeLevel = data.level;
changePage(1);
};
const typeData = ref([]);
const parentTypeData = ref([]);
const defaultProps = {
label: "classify_name",
children: "children",
value: "classify_id",
};
const getTypeTree = () => {
axios.get(`/apaas/knowledge/v5/documentmgr/classify/tree`).then((res) => {
if (res.data.code == 200) {
const result = res.data.data || [];
result.length ? (result[0].classify_id = "topLevel") : null;
typeData.value = result;
parentTypeData.value = getParentTree(typeData.value);
selectNode.value = typeData.value.length > 0 ? typeData.value[0].classify_id : "";
selectNodeObj.value = typeData.value.length > 0 ? typeData.value[0] : null;
nextTick(() => {
treeRef.value.setCurrentNode(selectNodeObj.value);
});
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
};
//过滤掉level不为0的层级
const getParentTree = (data) => {
let arr = [];
data.forEach((item) => {
if (item.level === 0) {
let obj = { classify_id: item.classify_id, classify_name: item.classify_name };
if (item.children) obj.children = getParentTree(item.children);
arr.push(obj);
}
});
return arr;
};
const filterNode = (value, data) => {
if (!value) return true;
return data.classify_name.includes(value);
};
const changeSize = (size) => {
state.filter.limit = size;
state.filter.page = 1;
changePage(1);
};
const changePage = (page) => {
state.filter.page = page;
getTableRows();
};
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 表格关键字筛选
const handleCloseForm = () => {
state.formData = {
parent_id: selectNode.value,
classify_name: "",
sort: "",
describe: "",
is_active: 0,
};
state.formDialogConfig.visible = false;
};
//在编辑状态中切换起停用状态
const beforeSwitchStatusInEdit = async () => {
if (state.formDialogConfig.type === "update" && state.selectedRow.is_active) {
const res = await axios.get(
`/apaas/knowledge/v5/documentmgr/classify/quote/${state.selectedRow.id}`
);
if (res.data.code !== 200) {
ElMessage.warning("请将该文档类型下所挂载的文档删除或转移至其他文档类型下再停用!");
return false;
}
return true;
}
return true;
};
const editCurType = async (row) => {
const res = await axios.get(`/apaas/knowledge/v5/documentmgr/classify/detail/${row.id}`);
if (res.data.code == 200) {
const data = res.data.data || {};
state.formData = {
parent_id: data.parent_id === topId ? "topLevel" : data.parent_id,
classify_name: data.classify_name,
sort: data.sort === 0 ? "" : data.sort,
describe: data.describe,
is_active: data.is_active,
};
state.selectedRow = row;
state.formDialogConfig = {
visible: true,
title: "编辑",
type: "update",
};
} else {
ElMessage.error(res.data.data);
}
};
const confirmForm = () => {
ruleForm.value.validate((valid, fields) => {
let params = {
...state.formData,
parent_id: state.formData.parent_id !== "topLevel" ? state.formData.parent_id : "",
sort: state.formData.sort === "" ? undefined : state.formData.sort,
};
if (state.formDialogConfig.type === "update") {
params = { ...params, id: state.selectedRow.id };
}
if (valid) {
axios
.post(`/apaas/knowledge/v5/documentmgr/classify/${state.formDialogConfig.type}`, params)
.then((res) => {
if (res.data.code == 200) {
getTypeTree();
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
handleCloseForm();
});
}
});
};
const handleCloseSecondTipsDialog = () => {
state.secondTipsDialogConfig.visible = false;
};
const deleteCurType = (row) => {
if (row.is_active === 1) return ElMessage.warning("该文档类型已启用,请先停用后再删除!");
state.selectedRow = row;
state.secondTipsDialogConfig = {
visible: true,
title: "删除",
type: "delete",
content: "确定删除该文档类型吗?",
};
};
//在列表中切换起停用状态
const beforeSwitchStatus = async (row) => {
if (row.is_active) {
const res = await axios.get(`/apaas/knowledge/v5/documentmgr/classify/quote/${row.id}`);
if (res.data.code !== 200) {
ElMessage.warning("请将该文档类型下所挂载的文档删除或转移至其他文档类型下再停用!");
return false;
}
}
const text = row.is_active ? "停用" : "启用";
state.selectedRow = row;
state.secondTipsDialogConfig = {
visible: true,
title: text,
type: "switchStatus",
content: `确定${text}该文档类型吗?`,
};
return false;
};
const secondConfirm = () => {
if (state.secondTipsDialogConfig.type === "delete") {
axios.delete(`/apaas/knowledge/v5/documentmgr/classify/${state.selectedRow.id}`).then((res) => {
if (res.data.code == 200) {
getTypeTree();
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
handleCloseSecondTipsDialog();
});
return;
}
if (state.secondTipsDialogConfig.type === "switchStatus") {
axios
.get(`/apaas/knowledge/v5/documentmgr/classify/active/${state.selectedRow.id}`)
.then((res) => {
if (res.data.code == 200) {
getTypeTree();
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
handleCloseSecondTipsDialog();
});
return;
}
};
</script>
<style lang="scss" scoped>
.tree_header {
height: 40px;
line-height: 40px;
background-color: #f7f7f9;
border-radius: 6px 6px 0px 0px;
font-size: 16px;
letter-spacing: 0px;
color: #202531;
padding-left: 16px;
}
.tree_content {
overflow: hidden;
height: calc(100% - 45px);
}
.search {
padding: 16px;
}
.tree {
overflow: auto;
height: calc(100% - 65px);
}
.tree :deep() .el-tree-node > .el-tree-node__children {
overflow: unset;
}
.tree :deep().noFocus:focus > .el-tree-node__content {
background-color: #fff;
}
.custom_tree_node {
width: 100%;
font-size: 14px;
color: #202531;
padding-right: 16px;
}
.label-text:hover {
padding: 3px 0;
background-color: var(--el-tree-node-hover-bg-color);
}
.main_container {
height: 100%;
}
.table_container {
height: calc(100% - 30px);
overflow: auto;
padding: 0 16px;
}
:deep() .el-switch__core {
min-width: 44px;
}
.el-form {
margin-top: 24px;
:deep() .el-select {
width: 100%;
.el-input .el-select__caret {
color: #b9b9b9;
}
}
}
.pagination_box {
position: sticky;
position: -webkit-sticky;
margin-top: 16px;
bottom: 25px;
background-color: #fff;
z-index: 1024;
height: 40px;
line-height: 40px;
padding-top: 5px;
}
.bg-pagination {
bottom: unset;
margin-top: 0px;
}
</style>
<!-- 角色管理 -->
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="form_content apaas_scroll_nor">
<el-form label-width="80px" :model="formData" :rules="rules" ref="ruleForm">
<el-form-item label="文档类型">
<el-input disabled v-model="docTypeName"> </el-input>
</el-form-item>
<el-form-item label="文档名称" prop="title">
<el-input v-model="formData.title" placeholder="请输入"> </el-input>
</el-form-item>
<el-form-item class="editor" label="文档内容" prop="content">
<mavon-editor
class="mark-down-edit"
ref="editorRef"
v-model="formData.content"
:box-shadow="false"
:autofocus="false"
placeholder="请输入正文"
@img-add="(...arg) => $imgAdd(arg, editorRef)" />
</el-form-item>
</el-form>
</div>
<div class="operate_btns">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="publish">发布</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, computed, ref, onBeforeMount, nextTick } from "vue";
import { useRoute, useRouter } from "vue-router";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { $imgAdd } from "../../helper";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const route = useRoute();
const router = useRouter();
const ruleForm = ref(null);
const editorRef = ref();
const state = reactive({
//表单数据
formData: {
classify_id: route.query.classify_id,
title: "",
content: "",
},
//表单校验规则
rules: {
title: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
},
docTypeNameArr: ["平台公开"],
});
const { formData, rules } = toRefs(state);
const docTypeName = computed(() => {
return state.docTypeNameArr.join("/");
});
onBeforeMount(() => {
getTypeTree();
});
const getTypeTree = () => {
axios.get(`/apaas/knowledge/v5/documentmgr/classify/tree`).then((res) => {
if (res.data.code == 200) {
const result = res.data.data || [];
getDocTypeName(result[0].children, route.query.classify_id);
} else {
ElMessage.error(res.data.data);
}
});
};
const getDocTypeName = (data, classifyId) => {
const findItem = data.find((item) => item.classify_id === classifyId);
if (findItem) {
state.docTypeNameArr.push(findItem.classify_name);
return true;
}
data.find((item) => {
if (item.children && item.children.length) {
const res = getDocTypeName(item.children, classifyId);
if (res) {
state.docTypeNameArr.splice(state.docTypeNameArr.length - 2, 0, item.classify_name);
}
return res;
}
});
};
//取消
const cancel = () => {
router.push("/support/document");
};
//发布
const publish = () => {
ruleForm.value.validate((valid, fields) => {
let params = {
...state.formData,
};
if (valid) {
axios.post(`/apaas/knowledge/v5/documentmgr/document/add`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
router.push("/support/document");
} else {
ElMessage.error(res.data.data);
}
});
}
});
};
</script>
<style lang="scss" scoped>
.page_container {
.main_container {
position: relative;
margin: 0 0 16px;
width: 100%;
height: calc(100% - 62px);
padding: 24px 0 70px;
overflow: auto;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
.form_content {
overflow: auto;
width: 60%;
min-width: 972px;
height: 100%;
padding-left: 35px;
}
.mark-down-edit {
height: 525px;
border: solid 1px #dadee7;
}
.operate_btns {
position: absolute;
bottom: 0;
width: 100%;
height: 70px;
text-align: right;
line-height: 68px;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
margin-right: 16px;
}
}
}
}
</style>
<!-- 角色管理 -->
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="main_container">
<div class="form_content apaas_scroll_nor">
<el-form label-width="80px" :model="formData" :rules="rules" ref="ruleForm">
<el-form-item label="文档类型">
<el-input disabled v-model="docTypeName"> </el-input>
</el-form-item>
<el-form-item label="文档名称" prop="title">
<el-input v-model="formData.title" disabled placeholder="请输入"> </el-input>
</el-form-item>
<el-form-item class="editor" label="文档内容" prop="content">
<mavon-editor
class="mark-down-edit"
ref="editorRef"
v-model="formData.content"
:box-shadow="false"
:autofocus="false"
placeholder="请输入正文"
@img-add="(...arg) => $imgAdd(arg, editorRef)" />
</el-form-item>
</el-form>
</div>
<div class="operate_btns">
<el-button @click="cancel">取消</el-button>
<el-button type="primary" @click="publish">发布</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, computed, ref, onBeforeMount, nextTick } from "vue";
import { useRoute, useRouter } from "vue-router";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import { $imgAdd } from "../../helper";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const route = useRoute();
const router = useRouter();
const ruleForm = ref(null);
const editorRef = ref();
const state = reactive({
//表单数据
formData: {
classify_id: route.query.classify_id,
title: "",
content: "",
},
//表单校验规则
rules: {
title: [
{
required: true,
message: "请输入",
trigger: "blur",
},
],
},
docTypeNameArr: ["平台公开"],
});
const { formData, rules } = toRefs(state);
const docTypeName = computed(() => {
return state.docTypeNameArr.join("/");
});
onBeforeMount(() => {
getTypeTree();
getEditInfo();
});
const getTypeTree = () => {
axios.get(`/apaas/knowledge/v5/documentmgr/classify/tree`).then((res) => {
if (res.data.code == 200) {
const result = res.data.data || [];
getDocTypeName(result[0].children, route.query.classify_id);
} else {
ElMessage.error(res.data.data);
}
});
};
const getDocTypeName = (data, classifyId) => {
const findItem = data.find((item) => item.classify_id === classifyId);
if (findItem) {
state.docTypeNameArr.push(findItem.classify_name);
return true;
}
data.find((item) => {
if (item.children && item.children.length) {
const res = getDocTypeName(item.children, classifyId);
if (res) {
state.docTypeNameArr.splice(state.docTypeNameArr.length - 2, 0, item.classify_name);
}
return res;
}
});
};
const getEditInfo = () => {
axios.get(`/apaas/knowledge/v5/documentmgr/document/detail/${route.query.id}`).then((res) => {
if (res.data.code == 200) {
const result = res.data.data || {};
Object.keys(state.formData).forEach((key) => {
state.formData[key] = result[key];
});
} else {
ElMessage.error(res.data.data);
}
});
};
//取消
const cancel = () => {
router.push("/support/document");
};
//发布
const publish = () => {
ruleForm.value.validate((valid, fields) => {
let params = {
id: +route.query.id,
content: state.formData.content,
};
if (valid) {
axios.post(`/apaas/knowledge/v5/documentmgr/document/update`, params).then((res) => {
if (res.data.code == 200) {
ElMessage.success(res.data.msg);
router.push("/support/document");
} else {
ElMessage.error(res.data.data);
}
});
}
});
};
</script>
<style lang="scss" scoped>
.page_container {
.main_container {
position: relative;
margin: 0 0 16px;
width: 100%;
height: calc(100% - 62px);
padding: 24px 0 70px;
overflow: auto;
background-color: #fff;
box-shadow: 0px 1px 4px 0px rgba(0, 7, 101, 0.15);
border-radius: 6px;
.form_content {
overflow: auto;
width: 60%;
min-width: 972px;
height: 100%;
padding-left: 35px;
}
.mark-down-edit {
height: 525px;
border: solid 1px #dadee7;
}
.operate_btns {
position: absolute;
bottom: 0;
width: 100%;
height: 70px;
text-align: right;
line-height: 68px;
border-top: solid 1px #e6e9ef;
.el-button {
width: 92px;
margin-right: 16px;
}
}
}
}
</style>
<template>
<div class="page_container">
<bg-breadcrumb></bg-breadcrumb>
<div class="flex_row">
<div class="flex_left bgc_white">
<div class="tree_header">文档类型</div>
<div class="tree_content">
<div class="search">
<el-input v-model="filterTree" placeholder="请输入文档名称" :prefix-icon="Search" />
</div>
<div class="tree">
<el-tree
ref="treeRef"
:data="typeData"
:props="defaultProps"
@node-click="handleNodeClick"
node-key="classify_id"
:highlight-current="true"
:filter-node-method="filterNode"
:default-expand-all="true">
<template #default="{ node, data }">
<div class="custom_tree_node">
<span class="label-text" :title="node.label">{{ node.label }} </span>
</div>
</template>
</el-tree>
</div>
</div>
</div>
<div class="flex_right bgc_white">
<div class="main_container">
<bg-filter-group
@search="changeSearch"
v-model="filter.search"
placeholder="请输入文档类型名称">
<template v-if="showAdd" v-slot:left_action>
<div class="apaas_button">
<el-button type="primary" @click="addDoc">
<bg-icon
style="font-size: 12px; color: #fff; margin-right: 8px"
icon="#bg-ic-add"></bg-icon>
新增
</el-button>
</div>
</template>
<template v-slot:filter_group>
<div class="left-filter filter_list">
<div class="filter_item">
<span class="filter_title">更新时间</span>
<el-date-picker
v-model="filter.time"
type="daterange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
style="width: 300px; flex: 0 0 auto" />
</div>
</div>
<div class="right-action apaas_button">
<el-button type="primary" @click="filterAction"> 查询 </el-button>
<el-button type="default" @click="filterClear"> 重置 </el-button>
</div>
</template>
</bg-filter-group>
<div class="table_container apaas_scroll">
<bg-table
ref="dataTable"
:headers="headers"
:rows="tableRows"
:isIndex="true"
:stripe="true">
<template v-slot:action="{ row }">
<bg-table-btn @click="editCurDoc(row)"> 编辑 </bg-table-btn>
<bg-table-btn @click="deleteCurDoc(row)"> 删除 </bg-table-btn>
</template>
</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>
<el-dialog v-model="deleteDialogVisible" title="删除" width="520px">
<div class="warning_info delete_tips">
<bg-icon
style="font-size: 12px; color: #a9b1c7; margin-right: 8px; vertical-align: baseline"
icon="#bg-ic-circle-tips"></bg-icon>
<span
>删除后,该文档将不再{{ selectedRow.classify_name }}里展示且<span style="color: red"
>删除后无法撤销</span
>,确定要删除吗?</span
>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleCloseDeleteDialog">取消</el-button>
<el-button type="primary" @click="confirmDelete">确定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import { watch, nextTick, reactive, toRefs, ref, computed, onBeforeMount } from "vue";
import { useRouter } from "vue-router";
import axios from "@/request/http.js";
import { ElMessage } from "element-plus";
import bgBreadcrumb from "@/components/bg-breadcrumb.vue";
const dataTable = ref(null);
const ruleForm = ref(null);
const selectNodeObj = ref({});
const filterTree = ref("");
const treeRef = ref(null);
const typeData = ref([]);
const defaultProps = {
label: "classify_name",
children: "children",
value: "classify_id",
};
//校验上级类型
const state = reactive({
filter: {
search: "",
time: [],
page: 1,
limit: 10,
},
headers: [
{
label: "文档名称",
prop: "title",
minWidth: 160,
},
{
label: "文档类型",
prop: "classify_name",
minWidth: 160,
},
{
label: "操作人",
prop: "updated_by",
},
{
label: "最后更新时间",
prop: "updated_time",
},
{
label: "操作",
prop: "action",
width: 220,
fixed: "right",
},
],
tableRows: [],
tableTotal: 0,
//是否展示新增按钮,只有当选择没有子节点的叶子节点才展示
showAdd: false,
//删除确认弹框
deleteDialogVisible: false,
selectedRow: null,
});
const { filter, headers, tableRows, tableTotal, showAdd, deleteDialogVisible, selectedRow } =
toRefs(state);
onBeforeMount(() => {
getTypeTree();
});
watch(filterTree, (val) => {
treeRef.value.filter(val);
});
const filterAction = () => {
changePage(1);
};
const filterClear = () => {
state.filter = {
search: "",
time: [],
page: 1,
limit: 10,
};
changePage(1);
};
const router = useRouter();
const getTableRows = () => {
let params = {
...state.filter,
start_at: state.filter.time ? state.filter.time[0] : undefined,
end_at: state.filter.time ? state.filter.time[1] : undefined,
classify_id: selectNodeObj.value.id ? selectNodeObj.value.id : undefined,
};
delete params.time;
axios
.get(`/apaas/knowledge/v5/documentmgr/document/list`, {
params,
})
.then((res) => {
if (res.data.code == 200) {
state.tableRows = res.data.data || [];
state.tableTotal = res.data.total;
} else {
ElMessage.error(res.data.data);
}
});
};
//新增文档
const addDoc = () => {
router.push({
path: "/support/document/add",
query: {
classify_id: selectNodeObj.value.classify_id,
},
});
};
const handleNodeClick = (data) => {
selectNodeObj.value = data;
state.showAdd = !(data.children && data.children.length);
changePage(1);
};
const getTypeTree = () => {
axios.get(`/apaas/knowledge/v5/documentmgr/classify/tree`).then((res) => {
if (res.data.code == 200) {
const result = res.data.data || [];
typeData.value = result;
selectNodeObj.value = typeData.value.length > 0 ? typeData.value[0] : {};
nextTick(() => {
treeRef.value.setCurrentNode(selectNodeObj.value);
});
changePage(1);
} else {
ElMessage.error(res.data.data);
}
});
};
const filterNode = (value, data) => {
if (!value) return true;
return data.classify_name.includes(value);
};
const changeSize = (size) => {
state.filter.limit = size;
state.filter.page = 1;
changePage(1);
};
const changePage = (page) => {
state.filter.page = page;
getTableRows();
};
const changeSearch = (val) => {
state.filter.search = val;
changePage(1);
}; // 表格关键字筛选
const editCurDoc = async (row) => {
router.push({
path: "/support/document/edit",
query: {
id: row.id,
classify_id: row.classify_id,
},
});
};
const handleCloseDeleteDialog = () => {
state.deleteDialogVisible = false;
};
const deleteCurDoc = (row) => {
state.selectedRow = row;
state.deleteDialogVisible = true;
};
const confirmDelete = () => {
axios.delete(`/apaas/knowledge/v5/documentmgr/classify/${state.selectedRow.id}`).then((res) => {
if (res.data.code == 200) {
changePage(1);
ElMessage.success(res.data.msg);
} else {
ElMessage.error(res.data.data);
}
handleCloseDeleteDialog();
});
};
</script>
<style lang="scss" scoped>
.tree_header {
height: 40px;
line-height: 40px;
background-color: #f7f7f9;
border-radius: 6px 6px 0px 0px;
font-size: 16px;
letter-spacing: 0px;
color: #202531;
padding-left: 16px;
}
.tree_content {
overflow: hidden;
height: calc(100% - 45px);
}
.search {
padding: 16px;
}
.tree {
overflow: auto;
height: calc(100% - 65px);
}
.tree :deep() .el-tree-node > .el-tree-node__children {
overflow: unset;
}
.tree :deep().noFocus:focus > .el-tree-node__content {
background-color: #fff;
}
.custom_tree_node {
width: 100%;
font-size: 14px;
color: #202531;
padding-right: 16px;
}
.label-text:hover {
padding: 3px 0;
background-color: var(--el-tree-node-hover-bg-color);
}
.main_container {
height: 100%;
}
.table_container {
height: calc(100% - 30px);
overflow: auto;
padding: 0 16px;
}
.delete_tips {
display: flex;
align-items: baseline;
line-height: 20px;
}
.pagination_box {
position: sticky;
position: -webkit-sticky;
margin-top: 16px;
bottom: 25px;
background-color: #fff;
z-index: 1024;
height: 40px;
line-height: 40px;
padding-top: 5px;
}
.bg-pagination {
bottom: unset;
margin-top: 0px;
}
</style>
import $axios from "@/request/http";
export const topId = "00000000-0000-0000-0000-000000000000";
export const $imgAdd = function (args, editorRef) {
const [pos, $file] = args;
const formdata = new FormData();
formdata.append("file", $file);
formdata.append("directory", "image");
$axios
.post("/apaas/common/image/upload", formdata, {
headers: { "Content-Type": "multipart/form-data" },
})
.then(({ data }) => {
editorRef.$img2Url(pos, data.data);
});
};
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