first commit
This commit is contained in:
@@ -0,0 +1,193 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="年度">
|
||||
<el-date-picker placeholder="请选择年度" type="year" v-model="pageForm.year" value-format="yyyy"></el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="活动主题">
|
||||
<el-input clearable placeholder="请输入活动主题" v-model="pageForm.searchKeyword"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<template slot="tool">
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openAdd">新增</el-button>
|
||||
</template>
|
||||
</table-tool>
|
||||
|
||||
<el-table :data="tableData" @sort-change="pageOrder" size="small" header-align="center" border style="width: 100%" row-key="id">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod"></el-table-column>
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="版本" prop="versionCode"></el-table-column>
|
||||
<el-table-column label="图标" prop="icon"></el-table-column>
|
||||
<el-table-column label="分类" prop="category"></el-table-column>
|
||||
<el-table-column label="排序" prop="sortCode"></el-table-column>
|
||||
<el-table-column prop="modelStatus" label="开启状态">
|
||||
<template scope="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.modelStatus"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
@change="(val)=>{enableChange(val,scope.row.id)}"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="250">
|
||||
<template slot-scope="scope">
|
||||
<el-link size="mini" type="primary" @click="openEdit(scope.row.id)">编辑</el-link>
|
||||
<el-link size="mini" type="primary" @click="design(scope.row.id)">设计</el-link>
|
||||
<el-link size="mini" type="success" @click="deploy(scope.row.id)">部署</el-link>
|
||||
<!-- <el-link size="mini" type="info" @click="doDelete(scope.row.id)">降级</el-link>-->
|
||||
<el-link size="mini" type="danger" @click="doDelete(scope.row.id)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-dialog :title="formData.id ? '编辑':'新增'" :visible.sync="dialogFormVisible" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="formData" ref="formRef" size="small" label-width="120px" :rules="formRules">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input placeholder="请输入名称" v-model="formData.name" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="模型分类" prop="category">
|
||||
<el-select placeholder="请选择分类" v-model="formData.category" clearable>
|
||||
<el-option v-for="item in categoryOptions" :key="item.id" :value="item.id" :label="item.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<el-input placeholder="请输入图标" v-model="formData.icon" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="颜色" prop="color">
|
||||
<el-input placeholder="请输入颜色" v-model="formData.color" clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="序号" prop="sortCode">
|
||||
<el-input-number placeholder="请输入序号" v-model="formData.sortCode" clearable></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
formRules: {
|
||||
name: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
|
||||
category: [{ required: true, message: "必填", trigger: ["change", "blur"] }]
|
||||
},
|
||||
categoryOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {
|
||||
subjectTypes: []
|
||||
}
|
||||
})
|
||||
},
|
||||
openEdit(id) {
|
||||
this.dialogFormVisible = true
|
||||
$.get(loc() + "/findOne", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
doSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
$.post(loc() + (this.formData.id ? "/update" : "/insert"), { data: JSON.stringify(this.formData) }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
$.post(loc() + "/delete", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
deploy(id) {
|
||||
this.$confirm("您确定要部署吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
$.post(loc() + "/deploy", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
enableChange(enable, id) {
|
||||
$.post(loc() + "/enableChange", { id, enable }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
},
|
||||
design(id) {
|
||||
window.open(loc() + "/modeler?id=" + id)
|
||||
},
|
||||
listCategory() {
|
||||
$.get(loc() + "/listCategory").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.categoryOptions = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.listCategory()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,152 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
#app {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
height: calc(100vh - 126px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#properties {
|
||||
background: #fff;
|
||||
border: 1px solid;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
#top-tool {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.djs-container {
|
||||
background-image: linear-gradient(90deg, hsla(0, 0%, 78.4%, 0.15) 10%, transparent 0),
|
||||
linear-gradient(hsla(0, 0%, 78.4%, 0.15) 10%, transparent 0);
|
||||
background-size: 10px 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/bpmn/assets/diagram-js.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/bpmn/assets/bpmn-font/css/bpmn-embedded.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/bpmn/assets/bpmn-js.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/bpmn/assets/bpmn-font/css/bpmn.css" />
|
||||
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/bpmn-js-properties-panel/properties-panel.css" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/@bpmn-io/properties-panel@3.23.0/dist/assets/properties-panel.css" />
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<!-- <el-dialog title="流程设计" width="100%" :visible.sync="dialogVisible">-->
|
||||
<div id="canvas"></div>
|
||||
<div id="properties"></div>
|
||||
<div id="top-tool">
|
||||
<el-button size="mini" @click="saveData">保存</el-button>
|
||||
<el-button size="mini" @click="openXML">查看xml</el-button>
|
||||
</div>
|
||||
<!-- </el-dialog>-->
|
||||
<el-dialog title="查看xml" width="50%" :visible.sync="xmlDialogVisible">{{xmlContent}}</el-dialog>
|
||||
</div>
|
||||
|
||||
<script src="${base!}/assets/platform/plugins/bpmn/js/bpmn-modeler.development.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/bpmn-js-properties-panel/bpmn-js-properties-panel.umd.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/bpmn-translate/index.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/bpmn-translate/customTranslate.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/camunda-bpmn-moddle/resources/camunda.js"></script>
|
||||
|
||||
<!--<script src="https://unpkg.com/bpmn-js@17.9.0/dist/bpmn-modeler.development.js"></script>-->
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
var bpmnModeler
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
modelId: GetQueryString("id"),
|
||||
|
||||
xmlDialogVisible: false,
|
||||
xmlContent: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
saveData() {
|
||||
bpmnModeler.saveXML().then((res) => {
|
||||
console.log(res)
|
||||
$.post("/platform/bpm/cfg/design/saveXml", {
|
||||
id: this.modelId,
|
||||
xml: res.xml
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
openXML() {
|
||||
bpmnModeler.saveXML().then((res) => {
|
||||
console.log(res)
|
||||
this.xmlDialogVisible = true
|
||||
this.xmlContent = res.xml
|
||||
})
|
||||
},
|
||||
|
||||
loadXML() {
|
||||
$.get("/platform/bpm/cfg/design/loadXml", { id: this.modelId }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (res.data) {
|
||||
bpmnModeler
|
||||
.importXML(res.data)
|
||||
.then(() => {
|
||||
console.log(1)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
} else {
|
||||
bpmnModeler.createDiagram()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
initModeler() {
|
||||
bpmnModeler = new BpmnJS({
|
||||
container: "#canvas",
|
||||
propertiesPanel: {
|
||||
parent: "#properties"
|
||||
},
|
||||
additionalModules: [
|
||||
BpmnJSPropertiesPanel.BpmnPropertiesPanelModule,
|
||||
BpmnJSPropertiesPanel.BpmnPropertiesProviderModule,
|
||||
BpmnJSPropertiesPanel.CamundaPlatformPropertiesProviderModule,
|
||||
BpmnJSPropertiesPanel.CamundaPlatformTooltipProvider,
|
||||
{
|
||||
translate: ["value", customTranslate]
|
||||
}
|
||||
],
|
||||
moddleExtensions: {
|
||||
camunda: camundaJson
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initModeler()
|
||||
this.loadXML()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,369 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.flowchart-example-container {
|
||||
width: 800px;
|
||||
height: 400px;
|
||||
background: white;
|
||||
border: 1px solid #bbb;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称">
|
||||
<el-input placeholder="请输入名称" v-model="pageForm.displayName"></el-input>
|
||||
</search-item>
|
||||
<search-item label="编码">
|
||||
<el-input placeholder="请输入编码" v-model="pageForm.name"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button size="mini" icon="el-icon-plus" type="primary" @click="openAdd">新增</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" border style="width: 100%" row-key="id">
|
||||
<el-table-column label="序号" width="50" type="index" :index="indexMethod"></el-table-column>
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="编码" prop="code"></el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template slot-scope="scope">
|
||||
<el-link size="mini" type="primary" @click="$refs.processViewerRef.onOpen(scope.row)">查看</el-link>
|
||||
<el-link size="mini" type="primary" @click="openEdit(scope.row)">编辑</el-link>
|
||||
<el-link size="mini" type="primary" @click="openNodeConfig(scope.row.id)">配置</el-link>
|
||||
<el-link size="mini" type="danger" @click="doDelete(scope.row.id)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="流程定义" width="30%" :close-on-click-modal="false" :visible.sync="dialogVisible">
|
||||
<el-form :model="formData" ref="form" label-width="80px">
|
||||
<el-form-item label="名称" prop="name" :rules="{required: true, message: '名称不能为空', trigger: 'blur'}">
|
||||
<el-input v-model="formData.name" placeholder="名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="编码" prop="code" :rules="{required: true, message: '编码不能为空', trigger: 'blur'}">
|
||||
<el-input v-model="formData.code" placeholder="编码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="URL" prop="url" :rules="{required: true, message: 'URL不能为空', trigger: 'blur'}">
|
||||
<el-input v-model="formData.url" placeholder="URL"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="H5URL" prop="h5url" :rules="{required: false, message: 'H5URL不能为空', trigger: 'blur'}">
|
||||
<el-input v-model="formData.h5url" placeholder="H5URL"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="节点配置" width="80%" :close-on-click-modal="false" :visible.sync="nodeDialogVisible">
|
||||
<el-form :model="nodeFormData" ref="nodeForm" label-width="120px" size="small">
|
||||
<el-form-item label="节点配置" prop="nodeDefine">
|
||||
<el-table :data="nodeFormData.nodes">
|
||||
<el-table-column label="名称" prop="nodeName" fixed="left" min-width="180">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.nodeName" max="20" clearable></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="编码" prop="nodeCode" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.nodeCode" max="20" clearable></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="节点类型" prop="nodeType" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.nodeType" clearable>
|
||||
<el-option value="START" label="开始"></el-option>
|
||||
<el-option value="TASK" label="任务"></el-option>
|
||||
<el-option value="BACK" label="退回"></el-option>
|
||||
<el-option value="END" label="结束"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="完成类型" prop="nodeApprovalType" width="130px">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.nodeApprovalType" clearable>
|
||||
<el-option value="NODE_APPLY" label="申请" v-if="row.nodeType==='START'"></el-option>
|
||||
<el-option value="NODE_PENDING" label="待审批" v-if="row.nodeType==='TASK'"></el-option>
|
||||
<el-option value="NODE_PASS" label="审批通过" v-if="row.nodeType==='END'"></el-option>
|
||||
<el-option value="NODE_REJECT" label="审批拒绝" v-if="row.nodeType==='END'"></el-option>
|
||||
<el-option value="NODE_BACK" label="审批退回" v-if="row.nodeType==='BACK'"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="参与类型" prop="nodePerformType" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.nodePerformType" clearable v-if="['TASK','START','BACK'].includes(row.nodeType)">
|
||||
<el-option value="ORDINARY" label="普通"></el-option>
|
||||
<el-option value="COUNTERSIGN" label="会签" v-if="row.nodeType==='TASK'"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="通过下个节点" prop="passNextNodeId">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.passNextNodeId" clearable v-if="['TASK','START'].includes(row.nodeType)">
|
||||
<el-option :value="item.id" :label="item.nodeName" :key="item.id" v-for="item in nodeFormData.nodes"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="拒绝下个节点" prop="rejectNextNodeId">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.rejectNextNodeId" clearable v-if="row.nodeType==='TASK'" :disabled="row.nodeType!=='TASK'">
|
||||
<el-option :value="item.id" :label="item.nodeName" :key="item.id" v-for="item in nodeFormData.nodes"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="退回下个节点" prop="backNextNodeId">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.backNextNodeId" clearable v-if="row.nodeType==='TASK'" :disabled="row.nodeType!=='TASK'">
|
||||
<el-option :value="item.id" :label="item.nodeName" :key="item.id" v-for="item in nodeFormData.nodes"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="动态决策表达式" prop="dynamicDecisions">
|
||||
<template slot-scope="{row}">
|
||||
<el-link type="primary" @click="openDynamicDecision(row)">点击配置</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="更多配置" prop="moreConfig">
|
||||
<template slot-scope="{row}">
|
||||
<el-link type="primary" @click="openMoreConfig(row)">更多配置</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column width="140px" fixed="right">
|
||||
<template #header>
|
||||
<el-button size="mini" icon="el-icon-plus" type="primary" @click="addNewNode">添加</el-button>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-link size="mini" type="danger" @click="nodeFormData.nodes.splice(scope.$index,1)">删除</el-link>
|
||||
<el-link size="mini" type="primary" @click="upNode(scope.$index)" :disabled="scope.$index===0">上移</el-link>
|
||||
<el-link
|
||||
size="mini"
|
||||
type="primary"
|
||||
@click="downNode(scope.$index)"
|
||||
:disabled="scope.$index===nodeFormData.nodes.length-1"
|
||||
>
|
||||
下移
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<!-- <el-button type="primary" @click="refreshNode">刷新</el-button>-->
|
||||
<div slot="footer">
|
||||
<el-button @click="nodeDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="doSubmitNodes">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="dynamicDecisionNodeTitle" width="50%" :close-on-click-modal="false" :visible.sync="dynamicDecisionNodeDialogVisible">
|
||||
<el-form :model="dynamicDecisionNodeFormData" ref="dynamicDecisionNodeForm" label-width="80px">
|
||||
<el-table :data="dynamicDecisionNodeFormData.dynamicDecisions">
|
||||
<el-table-column label="条件" prop="condition">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.condition" clearable></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="说明" prop="desc">
|
||||
<template slot-scope="{row}">
|
||||
<el-input v-model="row.desc" clearable></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下个节点" prop="toNodeId">
|
||||
<template slot-scope="{row}">
|
||||
<el-select v-model="row.toNodeId" clearable>
|
||||
<el-option :value="item.id" :label="item.nodeName" :key="item.id" v-for="item in nodeFormData.nodes"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column>
|
||||
<template #header>
|
||||
<el-button size="mini" icon="el-icon-plus" type="primary" @click="dynamicDecisionNodeFormData.dynamicDecisions.push({})">
|
||||
添加
|
||||
</el-button>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-link size="mini" type="danger" @click="dynamicDecisionNodeFormData.dynamicDecisions.splice(scope.$index,1)">删除</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="更多配置" width="50%" :close-on-click-modal="false" :visible.sync="moreConfigDialogVisible">
|
||||
<el-form :model="moreConfigFormData" label-width="150px">
|
||||
<el-form-item label="电脑端审核地址" prop="url" :rules="[{required:true,message:'请输入电脑端审核地址',trigger:['blur','change']}]">
|
||||
<el-input v-model="moreConfigFormData.url" clearable placeholder="请输入电脑端审核地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="移动端审核地址">
|
||||
<el-input v-model="moreConfigFormData.h5url" clearable placeholder="请输入移动端审核地址"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="moreConfigDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="doSubmitMoreConfig">确定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- <div id="chart_container">-->
|
||||
<!-- <div class="flowchart-example-container" id="flowchartworkspace"></div>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
nodeApprovalTypeOptions: [],
|
||||
formData: {},
|
||||
|
||||
nodeDialogVisible: false,
|
||||
nodeFormData: {
|
||||
id: null,
|
||||
nodes: []
|
||||
},
|
||||
|
||||
dynamicDecisionNodeDialogVisible: false,
|
||||
dynamicDecisionNodeFormData: {},
|
||||
|
||||
moreConfigDialogVisible: false,
|
||||
moreConfigFormData: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dynamicDecisionNodeTitle() {
|
||||
return this.dynamicDecisionNodeFormData.nodeName
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openAdd() {
|
||||
this.dialogVisible = true
|
||||
this.formData = {}
|
||||
},
|
||||
openEdit(row) {
|
||||
this.dialogVisible = true
|
||||
this.formData = { ...row }
|
||||
},
|
||||
|
||||
doSubmit() {
|
||||
this.$refs.form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$axios
|
||||
.post("/platform/bpm/processDefine/" + (this.formData.id ? "update" : "insert"), {
|
||||
define: JSON.stringify(this.formData)
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.dialogVisible = false
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/bpm/processDefine/delete", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
openNodeConfig(id) {
|
||||
this.nodeFormData.id = id
|
||||
this.nodeDialogVisible = true
|
||||
this.$axios.post("/platform/bpm/processDefine/nodes", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.nodeFormData.nodes = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
addNewNode() {
|
||||
this.$axios
|
||||
.post("/platform/bpm/processDefine/generateNodeId", {
|
||||
ids: JSON.stringify(this.nodeFormData.nodes.map((item) => item.id))
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.nodeFormData.nodes.push({
|
||||
id: res.data
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
openDynamicDecision(row) {
|
||||
this.dynamicDecisionNodeFormData = row
|
||||
if (!this.dynamicDecisionNodeFormData.dynamicDecisions) {
|
||||
this.dynamicDecisionNodeFormData.dynamicDecisions = []
|
||||
}
|
||||
this.dynamicDecisionNodeDialogVisible = true
|
||||
},
|
||||
doSubmitNodes() {
|
||||
this.$axios
|
||||
.post("/platform/bpm/processDefine/updateSubmitNodes", {
|
||||
define: JSON.stringify(this.nodeFormData)
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
openMoreConfig(row) {
|
||||
this.moreConfigFormData = { ...row }
|
||||
console.log(this.moreConfigFormData)
|
||||
this.moreConfigDialogVisible = true
|
||||
},
|
||||
doSubmitMoreConfig() {
|
||||
this.nodeFormData.nodes.forEach((item) => {
|
||||
if (item.id === this.moreConfigFormData.id) {
|
||||
item.url = this.moreConfigFormData.url
|
||||
item.h5url = this.moreConfigFormData.h5url
|
||||
}
|
||||
})
|
||||
this.moreConfigDialogVisible = false
|
||||
},
|
||||
|
||||
//上移node
|
||||
upNode(index) {
|
||||
this.nodeFormData.nodes.splice(index - 1, 0, this.nodeFormData.nodes.splice(index, 1)[0])
|
||||
},
|
||||
downNode(index) {
|
||||
this.nodeFormData.nodes.splice(index + 1, 0, this.nodeFormData.nodes.splice(index, 1)[0])
|
||||
},
|
||||
|
||||
refreshNode() {}
|
||||
},
|
||||
mounted() {},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,118 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.design-dialog .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="分类名称">
|
||||
<el-input v-model="pageForm.name" placeholder="请输入分类名称" clearable></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button type="primary" @click="onAdd" size="small" icon="el-icon-plus">新增</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
|
||||
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="name" label="分类名称"></el-table-column>
|
||||
<el-table-column prop="categoryCount" label="关联流程数"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="450px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="onEdit(row)">编辑</el-button>
|
||||
<el-button type="danger" size="mini" @click="onDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogVisible" width="40%">
|
||||
<el-form :model="formData" ref="formRef" label-width="80px">
|
||||
<el-form-item label="类型名称" prop="name"
|
||||
:rules="{ required: true, message: '请输入类型名称', trigger: 'blur' }">
|
||||
<el-input v-model="formData.name" placeholder="请输入类型名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="picIcon" label="图片图标">
|
||||
<file-upload
|
||||
:value.sync="formData.picIcon"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<el-input v-model="formData.icon" placeholder="请输入图标"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
formData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onAdd() {
|
||||
this.formData = {}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
onEdit(row) {
|
||||
this.dialogVisible = true
|
||||
this.formData = { ...row }
|
||||
},
|
||||
onSubmit() {
|
||||
this.$axios.post("/flow/category/submit", this.formData).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
this.dialogVisible = false
|
||||
}
|
||||
})
|
||||
},
|
||||
onDelete(row) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/category/delete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,49 @@
|
||||
<!--#
|
||||
layout("/layouts/v4/baseLayout.html"){
|
||||
#-->
|
||||
|
||||
<div id="flowCommonApprovalForm" v-cloak>
|
||||
<snaker-flow
|
||||
:task_id="taskId"
|
||||
:instance_id="instanceId"
|
||||
:business_id="businessId"
|
||||
:pjax_urls="{
|
||||
applicationInfo: '/platform/article/write/index_',
|
||||
taskForm: '/platform/article/write/index_'
|
||||
}"
|
||||
:pjax_config="{
|
||||
push: false,
|
||||
replace: false,
|
||||
timeout: 10000
|
||||
}"
|
||||
></snaker-flow>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#flowCommonApprovalForm",
|
||||
data() {
|
||||
return {
|
||||
taskId: null,
|
||||
instanceId: null,
|
||||
businessId: null,
|
||||
formConfig: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTaskSubmitted() {},
|
||||
handleCancel() {
|
||||
window.history.back()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.taskId = new URLSearchParams(window.location.search).get("taskId")
|
||||
this.instanceId = new URLSearchParams(window.location.search).get("instanceId")
|
||||
this.businessId = new URLSearchParams(window.location.search).get("businessId")
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,170 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.design-dialog .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
.el-table__row--level-1 {
|
||||
background-color: #FDF5E6 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="流程名称">
|
||||
<el-input v-model="pageForm.displayName" placeholder="请输入流程名称" clearable></el-input>
|
||||
</search-item>
|
||||
<search-item label="流程编码">
|
||||
<el-input v-model="pageForm.name" placeholder="请输入流程编码" clearable></el-input>
|
||||
</search-item>
|
||||
<search-item label="流程分类">
|
||||
<el-select v-model="pageForm.category" placeholder="请选择流程分类" style="width: 100%"
|
||||
@change="doSearch" clearable>
|
||||
<el-option v-for="item in categoryOptions" :label="item.name" :value="item.id" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool></table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%"
|
||||
row-key="id" :tree-props="{children: 'children', hasChildren: 'hasChildren'}">
|
||||
<el-table-column label="序号" type="index" width="60">
|
||||
<template v-slot="{ row }">
|
||||
{{ row.rowNum }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:key="column.prop"
|
||||
:width="column.width"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns"
|
||||
>
|
||||
<template v-slot="{ row }" v-if="column.prop === 'categoryName'">
|
||||
{{ categoryOptions.find(item => item.id === row.category)?.name }}
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'state'">
|
||||
<el-tag size="mini" v-if="row.state===1" type="success">启用</el-tag>
|
||||
<el-tag size="mini" v-else-if="row.state===0" type="info">停用</el-tag>
|
||||
</template>
|
||||
<template v-slot="{ row }" v-else-if="column.prop === 'createTime'">
|
||||
{{ $moment(row.createdAt).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="350px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" size="mini" @click="onView(row)">查看</el-button>
|
||||
<el-button v-if="row.state===1" type="danger" size="mini" @click="onDisable(row)">停用</el-button>
|
||||
<el-button v-if="row.state===0" type="primary" size="mini" @click="onEnable(row)">启用</el-button>
|
||||
<el-button v-if="row.state===0" type="danger" size="mini" @click="onDelete(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog title="查看流程图" :visible.sync="designVisible" class="design-dialog" width="80%">
|
||||
<div style="height: calc(100vh - 150px); width: 100%">
|
||||
<iframe v-if="designVisible" :src="designUrl" frameborder="0" height="100%" style="height: 100%; width: inherit"></iframe>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
categoryOptions: [],
|
||||
designVisible: false,
|
||||
designUrl: null,
|
||||
tableColumns: [
|
||||
{prop: 'displayName', label: '流程定义名称'},
|
||||
{prop: 'name', label: '流程定义编码'},
|
||||
{prop: 'categoryName', label: '流程分类'},
|
||||
{prop: 'version', label: '版本号'},
|
||||
{prop: 'state', label: '状态'},
|
||||
{prop: 'createTime', label: '创建时间'},
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onView(row) {
|
||||
this.designVisible = true
|
||||
this.designUrl = "/flow/define/preview?defineId=" + row.id
|
||||
},
|
||||
|
||||
onDisable(row) {
|
||||
this.$confirm("确定要停用该流程吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/define/disable", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("停用成功")
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
onEnable(row) {
|
||||
this.$confirm("确定要启用该流程吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/define/enable", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("启用成功")
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onDelete(row) {
|
||||
this.$confirm("您确定要删除吗", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/define/delete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
listCategory() {
|
||||
this.$axios.post("/flow/category/list").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.categoryOptions = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.listCategory()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,360 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0"/>
|
||||
<title>流程设计器</title>
|
||||
<script src="/assets/platform/plugins/vue/vue.js"></script>
|
||||
<script src="/assets/platform/plugins/jquery/jquery.js"></script>
|
||||
<script src="/assets/platform/plugins/element-ui/lib/index.js"></script>
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css"/>
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css"/>
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/override.css"/>
|
||||
<!-- 引入 core 包和对应 css-->
|
||||
<script src="/assets/platform/plugins/logicflow/logic-flow.js"></script>
|
||||
<link rel="stylesheet" href="/assets/platform/plugins/logicflow/index.css"/>
|
||||
<script src="/assets/platform/plugins/logicflow/index.min.js"></script>
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@logicflow/extension@2.1.4/dist/index.min.js"></script>-->
|
||||
<script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>
|
||||
<style>
|
||||
#snaker-flow-preview {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.approval-card {
|
||||
position: absolute;
|
||||
width: 320px;
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #d9e3f0;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
z-index: 1000;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
|
||||
transform: translate(10px, 10px);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab-container {
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.tab-header {
|
||||
display: flex;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.tab-item {
|
||||
padding: 8px 16px;
|
||||
cursor: pointer;
|
||||
border-right: 1px solid #e8e8e8;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
background: #fafafa;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tab-item:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.tab-item.active {
|
||||
background: #fff;
|
||||
color: #333;
|
||||
border-bottom: 2px solid #1890ff;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.tab-item:hover {
|
||||
background: #f0f0f0;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 卡片标题栏(OA风格顶部色条) */
|
||||
.approval-card .card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: #1a73e8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.approval-card .card-header h3 {
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.approval-card .card-header button {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border: none;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.approval-card .card-header button:hover {
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 卡片内容区域 */
|
||||
.approval-card .card-body {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
/* 信息行样式 */
|
||||
.approval-card .info-row {
|
||||
display: flex;
|
||||
margin-bottom: 12px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.approval-card .info-label {
|
||||
width: 80px;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.approval-card .info-value {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
/*!* 分隔线 *!*/
|
||||
/*.approval-card .divider {*/
|
||||
/* height: 1px;*/
|
||||
/* background: #f0f0f0;*/
|
||||
/* margin: 12px 0;*/
|
||||
/*}*/
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="snaker-flow-preview">
|
||||
<snaker-flow-designer
|
||||
ref="designer"
|
||||
v-model="flowData"
|
||||
:show-doc="false"
|
||||
:viewer="true"
|
||||
node-render-type="html"
|
||||
:high-light="highLight"
|
||||
></snaker-flow-designer>
|
||||
|
||||
<div
|
||||
v-if="showApprovalCard"
|
||||
class="approval-card"
|
||||
:style="{
|
||||
left: cardPosition.x+'px',
|
||||
top: cardPosition.y+'px'
|
||||
}"
|
||||
>
|
||||
<div class="card-header">
|
||||
<h3>详情</h3>
|
||||
<button @click="closeCard">×</button>
|
||||
</div>
|
||||
<!-- Tab切换 -->
|
||||
<div class="tab-container" v-if="approvalInfos.length > 1">
|
||||
<div class="tab-header">
|
||||
<div
|
||||
v-for="(item, index) in approvalInfos"
|
||||
:key="index"
|
||||
class="tab-item"
|
||||
:class="{active: activeTabIndex === index}"
|
||||
@click="activeTabIndex = index"
|
||||
>
|
||||
记录{{index + 1}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div v-for="(approvalInfo, index) in approvalInfos" :key="index" v-show="activeTabIndex === index">
|
||||
<div class="info-row">
|
||||
<span class="info-label">节点类型:</span>
|
||||
<span class="info-value">{{approvalInfo.displayName}}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">审批人:</span>
|
||||
<span class="info-value" v-if="currentNode.id === 'startTask'">
|
||||
{{approvalInfo.ext?.initiatorName}}({{approvalInfo.ext?.initiatorAccount}})
|
||||
</span>
|
||||
<span class="info-value" v-else-if="approvalInfo.taskState === 10">
|
||||
{{ approvalInfo.taskActorList.map(u => u.actorName + '(' + u.actorAccount + ')').join('、') }}
|
||||
</span>
|
||||
<span class="info-value"
|
||||
v-else>{{approvalInfo.taskFormData.userName}}({{approvalInfo.taskFormData?.loginName}})</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">审核状态:</span>
|
||||
<span class="info-value">{{getTaskStateText(approvalInfo.taskState)}}</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">提交结果:</span>
|
||||
<span class="info-value" v-if="approvalInfo.taskState !== 10">
|
||||
{{getSubmitTypeText(approvalInfo.ext.submitType)}}
|
||||
</span>
|
||||
<span class="info-value" v-else>
|
||||
暂未处理
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">提交意见:</span>
|
||||
<span class="info-value" v-if="approvalInfo.taskState !== 10">{{approvalInfo.taskFormData.opinion}}</span>
|
||||
<span class="info-value" v-else>暂未处理</span>
|
||||
</div>
|
||||
<!-- <div class="divider"></div>-->
|
||||
<div class="info-row">
|
||||
<span class="info-label">提交时间:</span>
|
||||
<span class="info-value">{{approvalInfo.finishTime || '暂未处理'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script>
|
||||
Vue.use(SnakerflowDesigner.default)
|
||||
const vue = new Vue({
|
||||
el: "#snaker-flow-preview",
|
||||
data() {
|
||||
return {
|
||||
// 流程定义ID
|
||||
defineId: "${defineId!}",
|
||||
// 流程实例ID
|
||||
instanceId: "${instanceId!}",
|
||||
// 流程定义数据
|
||||
flowData: {},
|
||||
// 高亮数据
|
||||
highLight: {},
|
||||
// 历史审批记录
|
||||
hisApproval: [],
|
||||
// 展示已审核信息
|
||||
showApprovalCard: false,
|
||||
// 审批信息卡片位置
|
||||
cardPosition: {x: 0, y: 0},
|
||||
// 当前节点
|
||||
currentNode: [],
|
||||
// 审批信息数组
|
||||
approvalInfos: [],
|
||||
// 当前选中的tab索引
|
||||
activeTabIndex: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// approvalInfo() {
|
||||
// if (this.currentNode) {
|
||||
// return this.hisApproval.find((item) => item.taskName === this.currentNode.id)
|
||||
// }
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
getDetail() {
|
||||
$.get("/flow/define/detail", {id: this.defineId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.flowData = res.data.content
|
||||
this.initNodeEvent()
|
||||
}
|
||||
})
|
||||
},
|
||||
getHighLight() {
|
||||
$.get("/flow/common/instanceHighLight", {instanceId: this.instanceId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.highLight = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getHisApproval() {
|
||||
$.get("/flow/common/approvalRecord", {instanceId: this.instanceId}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.hisApproval = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
initNodeEvent() {
|
||||
this.$nextTick(() => {
|
||||
console.log(this.$refs.designer.lf.graphModel.eventCenter)
|
||||
this.$refs.designer.lf.graphModel.eventCenter.on("node:click", ({e, data}) => {
|
||||
console.log(data)
|
||||
console.log(e)
|
||||
|
||||
// 获取点击位置(考虑页面滚动情况)
|
||||
const scrollX = window.scrollX || window.pageXOffset
|
||||
const scrollY = window.scrollY || window.pageYOffset
|
||||
|
||||
this.cardPosition = {
|
||||
x: e.clientX + scrollX,
|
||||
y: e.clientY + scrollY
|
||||
}
|
||||
|
||||
if (data.type !== "snaker:task") return
|
||||
|
||||
const approvalInfos = this.hisApproval.filter((item) => item.taskName === data.id && [10, 20].includes(item.taskState))
|
||||
|
||||
if (approvalInfos && approvalInfos.length > 0) {
|
||||
this.approvalInfos = approvalInfos
|
||||
this.currentNode = data
|
||||
this.activeTabIndex = 0
|
||||
this.showApprovalCard = true
|
||||
}
|
||||
})
|
||||
|
||||
this.$refs.designer.lf.graphModel.eventCenter.on("blank:click", (args) => {
|
||||
this.closeCard()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
closeCard() {
|
||||
this.showApprovalCard = false
|
||||
this.activeTabIndex = 0
|
||||
},
|
||||
|
||||
// 获取任务状态文本
|
||||
getTaskStateText(taskState) {
|
||||
const stateMap = {
|
||||
10: "进行中",
|
||||
20: "已完成",
|
||||
30: "已撤回"
|
||||
}
|
||||
return stateMap[taskState] || taskState
|
||||
},
|
||||
|
||||
// 获取提交结果文本
|
||||
getSubmitTypeText(submitType) {
|
||||
const typeMap = {
|
||||
0: "发起申请",
|
||||
1: "同意申请",
|
||||
2: "拒绝申请",
|
||||
3: "退回上一步",
|
||||
4: "跳转",
|
||||
5: "重新提交",
|
||||
6: "退回发起人",
|
||||
20: "拒绝申请"
|
||||
}
|
||||
return typeMap[submitType] || submitType
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.defineId) {
|
||||
this.getDetail()
|
||||
}
|
||||
if (this.instanceId) {
|
||||
this.getHighLight()
|
||||
this.getHisApproval()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,185 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>流程设计</title>
|
||||
<link rel="stylesheet" href="/assets/platform/plugins/snaker/snaker.css" type="text/css" media="all" />
|
||||
</head>
|
||||
<body class="pear-container">
|
||||
<div id="toolbox">
|
||||
<div id="toolbox_handle">工具集</div>
|
||||
<div class="node" id="save">
|
||||
<img src="/assets/platform/plugins/snaker/images/save.gif" />
|
||||
保存
|
||||
</div>
|
||||
<div>
|
||||
<hr />
|
||||
</div>
|
||||
<div class="node selectable" id="pointer">
|
||||
<img src="/assets/platform/plugins/snaker/images/select16.gif" />
|
||||
Select
|
||||
</div>
|
||||
<div class="node selectable" id="path">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/flow_sequence.png" />
|
||||
transition
|
||||
</div>
|
||||
<div>
|
||||
<hr />
|
||||
</div>
|
||||
<div class="node state" id="start" type="start">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/start_event_empty.png" />
|
||||
start
|
||||
</div>
|
||||
<div class="node state" id="end" type="end">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/end_event_terminate.png" />
|
||||
end
|
||||
</div>
|
||||
<div class="node state" id="task" type="task">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/task_empty.png" />
|
||||
task
|
||||
</div>
|
||||
<div class="node state" id="task" type="custom">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/task_empty.png" />
|
||||
custom
|
||||
</div>
|
||||
<div class="node state" id="task" type="subprocess">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/task_empty.png" />
|
||||
subprocess
|
||||
</div>
|
||||
<div class="node state" id="fork" type="decision">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/gateway_exclusive.png" />
|
||||
decision
|
||||
</div>
|
||||
<div class="node state" id="fork" type="fork">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/gateway_parallel.png" />
|
||||
fork
|
||||
</div>
|
||||
<div class="node state" id="join" type="join">
|
||||
<img src="/assets/platform/plugins/snaker/images/16/gateway_parallel.png" />
|
||||
join
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="properties">
|
||||
<div id="properties_handle">属性</div>
|
||||
<table class="properties_all" cellpadding="0" cellspacing="0"></table>
|
||||
<div> </div>
|
||||
</div>
|
||||
|
||||
<div id="snakerflow"></div>
|
||||
|
||||
<script src="/assets/platform/plugins/jquery/jquery.js"></script>
|
||||
<script src="/assets/platform/plugins/snaker/raphael-min.js" type="text/javascript"></script>
|
||||
<script src="/assets/platform/plugins/snaker/jquery-ui-1.8.4.custom/js/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="/assets/platform/plugins/snaker/jquery-ui-1.8.4.custom/js/jquery-ui.min.js" type="text/javascript"></script>
|
||||
<script src="/assets/platform/plugins/snaker/dialog.js" type="text/javascript"></script>
|
||||
<script src="/assets/platform/plugins/snaker/snaker.designer.js" type="text/javascript"></script>
|
||||
<script src="/assets/platform/plugins/snaker/snaker.model.js" charset="utf-8" type="text/javascript"></script>
|
||||
<script src="/assets/platform/plugins/snaker/snaker.editors.js" type="text/javascript"></script>
|
||||
<script nonce="${cspNonce!}">
|
||||
const processId = "${processId!}"
|
||||
console.log(processId)
|
||||
|
||||
console.log(new URLSearchParams(window.location.search).get("processId"))
|
||||
|
||||
$.post("/snakerflow/define//process/modelJson", { processId }).then((res) => {
|
||||
console.log(res)
|
||||
if (res.code === 0) {
|
||||
let model = res.data
|
||||
if (model) {
|
||||
model = eval("(" + model + ")")
|
||||
}
|
||||
$("#snakerflow").snakerflow({
|
||||
basePath: "/assets/platform/plugins/snaker/",
|
||||
ctxPath: "/",
|
||||
restore: model,
|
||||
userJson: {},
|
||||
formPath: "forms/",
|
||||
tools: {
|
||||
save: {
|
||||
onclick: function (data) {
|
||||
saveModel(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
function saveModel(data) {
|
||||
$.post("/snakerflow/define/saveProcessXml", { model: data, id: processId }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
// alert("保存成功")
|
||||
window.parent.postMessage("success")
|
||||
} else {
|
||||
window.parent.postMessage("error")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// layui.use(['table', 'form', 'jquery', 'common', 'easyAdmin'], function () {
|
||||
// let easyAdmin = layui.easyAdmin;
|
||||
// id = getQueryString("processId");
|
||||
// easyAdmin.httpGet("/flow/process/modelJson?processId=" + id, function (data) {
|
||||
// var model = "";
|
||||
// if (data) {
|
||||
// model = eval("(" + data + ")");
|
||||
// }
|
||||
// var userJson = {};
|
||||
// easyAdmin.httpGet("/sys/user/getAll", function (result) {
|
||||
// if (result.success) {
|
||||
// userJson = result.data;
|
||||
// }
|
||||
// }, true, false);
|
||||
//
|
||||
// var isSubmitting = false; // 添加一个变量用于标记是否正在提交中
|
||||
//
|
||||
// $('#snakerflow').snakerflow({
|
||||
// basePath: "/assets/platform/plugins/snaker/",
|
||||
// ctxPath: "/",
|
||||
// restore: model,
|
||||
// userJson: userJson,
|
||||
// formPath: "forms/",
|
||||
// tools: {
|
||||
// save: {
|
||||
// onclick: function (data) {
|
||||
// if (isSubmitting) {
|
||||
// return false; // 如果正在提交中,则不执行后续代码
|
||||
// }
|
||||
// isSubmitting = true; // 设置为正在提交中状态
|
||||
// saveModel(data);
|
||||
// isSubmitting = false; // 处理完成后重置提交状态
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
//
|
||||
//
|
||||
// function getQueryString(name) {
|
||||
// var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
|
||||
// var r = window.location.search.substr(1).match(reg);
|
||||
// if (r !== null)
|
||||
// return unescape(r[2]);
|
||||
// return "";
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // window.saveModel = function (data) {
|
||||
// // easyAdmin.http({
|
||||
// // type: 'POST',
|
||||
// // url: "/flow/process/deployXml",
|
||||
// // data: "model=" + data + "&id=" + id,
|
||||
// // async: false,
|
||||
// // globle: false,
|
||||
// // success: function (data) {
|
||||
// // parent.layer.close(parent.layer.getFrameIndex(window.name));//关闭当前页
|
||||
// // parent.layui.table.reload("user-table");
|
||||
// // }
|
||||
// // });
|
||||
// //
|
||||
// // }
|
||||
// })
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,235 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.design-dialog .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称">
|
||||
<el-input v-model="pageForm.displayName" placeholder="请输入名称" clearable></el-input>
|
||||
</search-item>
|
||||
<search-item label="编码">
|
||||
<el-input v-model="pageForm.name" placeholder="请输入编码" clearable></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button type="primary" @click="onAdd" size="small" icon="el-icon-plus">新增</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
|
||||
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="displayName" label="流程定义名称"></el-table-column>
|
||||
<el-table-column prop="name" label="流程定义编码"></el-table-column>
|
||||
<el-table-column prop="categoryName" label="流程分类">
|
||||
<template slot-scope="{row}">{{categoryOptions.find(item => item.id ===
|
||||
row.category)?.categoryName}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="version" label="版本号"></el-table-column>
|
||||
<el-table-column prop="state" label="状态">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" v-if="row.state===1" type="success">启用</el-tag>
|
||||
<el-tag size="mini" v-else-if="row.state===0" type="info">停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间"></el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="350px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="onDesign(row)">设计</el-button>
|
||||
</el-button>
|
||||
<el-button size="mini" type="primary" @click="onEdit(row)">编辑</el-button>
|
||||
<!-- <el-button type="text" size="mini" @click="onView(row)">查看</el-button>-->
|
||||
<el-button v-if="row.state===1" type="danger" size="mini" @click="onUndeploy(row)">停用
|
||||
</el-button>
|
||||
<el-button v-if="row.state===0" type="primary" size="mini" @click="onPublish(row)">发布
|
||||
</el-button>
|
||||
<el-button v-if="row.state===0" type="danger" size="mini" @click="onDelete(row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogVisible" width="800px">
|
||||
<el-form :model="formData" ref="formRef" label-width="120px">
|
||||
<el-form-item label="流程定义名称" prop="flowName" :rules="{ required: true, message: '请输入流程定义名称', trigger: 'blur' }">
|
||||
<el-input v-model="formData.flowName" placeholder="请输入流程定义名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程定义编码" prop="flowCode" :rules="{ required: true, message: '请输入流程定义编码', trigger: 'blur' }">
|
||||
<el-input v-model="formData.flowCode" placeholder="请输入流程定义编码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程分类" prop="category" :rules="{ required: true, message: '请选择流程分类', trigger: 'change' }">
|
||||
<el-select v-model="formData.category" placeholder="请选择流程分类" clearable>
|
||||
<el-option v-for="item in categoryOptions" :key="item.id" :label="item.categoryName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="流程设计" :visible.sync="designVisible" fullscreen class="design-dialog">
|
||||
<div style="height: calc(100vh - 55px); width: 100%">
|
||||
<iframe v-if="designVisible" :src="designUrl" frameborder="0" height="100%"
|
||||
style="height: 100%; width: inherit"></iframe>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
|
||||
<el-dialog title="xml编辑" :visible.sync="xmlVisible" width="70%">
|
||||
<el-input type="textarea" style="width: 100%;" autosize v-model="formData.model"></el-input>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="xmlVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSave">保存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
categoryOptions: [],
|
||||
designVisible: false,
|
||||
designUrl: null,
|
||||
xmlVisible: false,
|
||||
formData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onAdd(){
|
||||
this.formData = {}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
onSubmit(){
|
||||
$.post("/snakerflow/define/addProcess", this.formData)
|
||||
},
|
||||
|
||||
|
||||
onDesign(row = null) {
|
||||
this.designVisible = true
|
||||
this.iframeLoaded()
|
||||
this.designUrl = "/flow/define/designer2?processId=" + row?.id
|
||||
},
|
||||
|
||||
onEdit(row) {
|
||||
this.xmlVisible = true
|
||||
this.$axios.post("/snakerflow/define/getXml", { id: row.id }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.formData = {
|
||||
id: row.id,
|
||||
model: res.data
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onSave() {
|
||||
$.post("/snakerflow/define/saveProcessXml", { ...this.formData, xmlHeader: true }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.pageData()
|
||||
this.xmlVisible = false
|
||||
} else {
|
||||
this.$message.error("保存失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
iframeLoaded() {
|
||||
// iframe监听组件内设计器保存事件
|
||||
window.onmessage = (event) => {
|
||||
console.log(event)
|
||||
if (event.data === "success") {
|
||||
this.$message.success("保存成功")
|
||||
this.designVisible = false
|
||||
this.pageData()
|
||||
} else if (event.data === "error") {
|
||||
this.$message.error("保存失败")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onUndeploy(row) {
|
||||
this.$confirm("您确定要停用吗", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/snakerflow/define/undeploy", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onPublish(row) {
|
||||
this.$confirm("您确定要发布吗", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/snakerflow/define/publish", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onDelete(row) {
|
||||
this.$confirm("您确定要删除吗", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/snakerflow/define/delete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onView(row) {
|
||||
},
|
||||
|
||||
listCategory() {
|
||||
// this.$axios.get("/platform/warmFlow/category/list").then((res) => {
|
||||
// if (res.code === 0) {
|
||||
// this.categoryOptions = res.data
|
||||
// }
|
||||
// })
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.listCategory()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,252 @@
|
||||
var assigneeForm = {
|
||||
template: /*language=HTML*/ `
|
||||
<el-form-item label="参与者">
|
||||
<div>
|
||||
<!-- 模式选择 -->
|
||||
<div style="margin-bottom: 10px;">
|
||||
<el-radio-group v-model="assigneeMode" @change="handleModeChange" size="small">
|
||||
<el-radio-button label="manual" :disabled="isFirstTaskNode">手动输入</el-radio-button>
|
||||
<el-radio-button label="select" :disabled="isFirstTaskNode">选择输入</el-radio-button>
|
||||
<el-radio-button label="initiator">流程发起人</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
|
||||
<!-- 手动输入模式 -->
|
||||
<el-input
|
||||
v-if="assigneeMode === 'manual'"
|
||||
v-model="manualInput"
|
||||
placeholder="请输入参与者,多个参与者用逗号分隔"
|
||||
@change="updateModelValue">
|
||||
</el-input>
|
||||
|
||||
<!-- 选择输入模式 -->
|
||||
<el-input
|
||||
v-if="assigneeMode === 'select'"
|
||||
v-model="assigneeText"
|
||||
readonly
|
||||
style="width: 100%"
|
||||
placeholder="请选择参与者">
|
||||
<el-button
|
||||
slot="append"
|
||||
type="primary"
|
||||
style="color: #fff;background-color: var(--color-primary);border-color: var(--color-primary)"
|
||||
icon="el-icon-setting"
|
||||
@click="openUserDialog">
|
||||
选择
|
||||
</el-button>
|
||||
</el-input>
|
||||
|
||||
<!-- 流程发起人模式 -->
|
||||
<el-input
|
||||
v-if="assigneeMode === 'initiator'"
|
||||
value="initiator"
|
||||
readonly
|
||||
disabled
|
||||
style="width: 100%"
|
||||
placeholder="流程发起人">
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<!-- 用户选择弹窗 -->
|
||||
<el-dialog title="选择参与者" :visible.sync="dialogVisible" width="80%" append-to-body top="20px">
|
||||
<div style="display: flex;">
|
||||
<!-- 左侧表格 -->
|
||||
<div style="flex: 3; margin-right: 10px; overflow: auto;">
|
||||
<div style="margin-bottom: 10px;">
|
||||
<el-input v-model="pageForm.searchKeyword" placeholder="请输入姓名或工号搜索" style="width: 200px; margin-right: 10px;"></el-input>
|
||||
<el-button type="primary" @click="searchUsers">搜索</el-button>
|
||||
</div>
|
||||
<el-table
|
||||
ref="userTableRef"
|
||||
:data="userList"
|
||||
border
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column type="selection" width="55" :reserve-selection="true"></el-table-column>
|
||||
<el-table-column prop="loginname" label="工号" width="100"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" width="120"></el-table-column>
|
||||
<el-table-column prop="unitName" label="单位"></el-table-column>
|
||||
</el-table>
|
||||
<div style="margin-top: 10px; text-align: right;">
|
||||
<el-pagination
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount">
|
||||
</el-pagination>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧已选列表 -->
|
||||
<div style="flex: 1; border: 1px solid #EBEEF5; border-radius: 4px; padding: 10px; overflow: auto;" v-if="$refs.userTableRef">
|
||||
<div style="font-weight: bold; margin-bottom: 10px;">已选择的参与者</div>
|
||||
<el-tag
|
||||
v-for="user in $refs.userTableRef.selection"
|
||||
:key="user.id"
|
||||
style="margin: 0 5px 5px 0;">
|
||||
{{ user.username }}
|
||||
</el-tag>
|
||||
<div v-if="$refs.userTableRef.selection.length === 0" style="color: #909399; font-size: 14px;">
|
||||
暂无选择的参与者
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="confirmSelection">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</el-form-item>
|
||||
`,
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
field: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
start_node_next_node_ids: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
/**
|
||||
* 判断当前节点是否为第一个任务节点
|
||||
* @returns {boolean}
|
||||
*/
|
||||
isFirstTaskNode() {
|
||||
return this.start_node_next_node_ids.includes(this.model.name)
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
assigneeMode: "manual",
|
||||
manualInput: "",
|
||||
assigneeText: "",
|
||||
dialogVisible: false,
|
||||
userList: [],
|
||||
selectedUsers: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
searchKeyword: "",
|
||||
totalCount: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 回显
|
||||
this.initFromModel()
|
||||
},
|
||||
methods: {
|
||||
initFromModel() {
|
||||
const value = this.model[this.field]
|
||||
this.assigneeText = this.model["assigneeText"] || ""
|
||||
this.assigneeMode = this.model["assigneeMode"] || "manual"
|
||||
|
||||
if (this.assigneeMode === "initiator") {
|
||||
this.assignee = "initiator"
|
||||
this.assigneeText = "流程发起人"
|
||||
}
|
||||
|
||||
if (this.assigneeMode === "manual") {
|
||||
this.manualInput = value || ""
|
||||
}
|
||||
},
|
||||
|
||||
handleModeChange() {
|
||||
this.assigneeText = ""
|
||||
this.manualInput = ""
|
||||
this.assignee = ""
|
||||
|
||||
// 如果切换到流程发起人模式,直接设置值
|
||||
if (this.assigneeMode === "initiator") {
|
||||
this.assignee = "{$initiator}"
|
||||
this.assigneeText = "流程发起人"
|
||||
}
|
||||
|
||||
this.updateModelValue()
|
||||
},
|
||||
openUserDialog() {
|
||||
this.dialogVisible = true
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.searchKeyword = ""
|
||||
this.loadSelectUsers()
|
||||
},
|
||||
|
||||
loadSelectUsers() {
|
||||
let userIds = this.model[this.field].split(",")
|
||||
$.get("/flow/design/assigneeEcho", { userIds: JSON.stringify(userIds) }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.selectedUsers = res.data
|
||||
this.loadUsers()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
loadUsers() {
|
||||
$.get("/flow/design/assigneePage", {
|
||||
...this.pageForm
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.userList = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
// 设置已选中的行
|
||||
this.$nextTick(() => {
|
||||
this.setTableSelection()
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 设置表格选中数据
|
||||
setTableSelection() {
|
||||
// 如果当前表格中有该用户,选中TA
|
||||
this.userList.forEach((row) => {
|
||||
const exist = this.selectedUsers.map((item) => item.id).includes(row.id)
|
||||
if (exist) {
|
||||
this.$refs.userTableRef.toggleRowSelection(row, true)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
confirmSelection() {
|
||||
const selection = this.$refs.userTableRef.selection
|
||||
this.assignee = selection.map((item) => item.id).join(",")
|
||||
this.assigneeText = selection.map((item) => item.username).join(",")
|
||||
this.updateModelValue()
|
||||
this.dialogVisible = false
|
||||
},
|
||||
updateModelValue() {
|
||||
// 更新model中的值
|
||||
if (this.assigneeMode === "manual") {
|
||||
this.assignee = this.manualInput
|
||||
this.assigneeText = this.manualInput
|
||||
} else if (this.assigneeMode === "initiator") {
|
||||
this.assignee = "initiator"
|
||||
this.assigneeText = "流程发起人"
|
||||
}
|
||||
// 选择模式的处理在confirmSelection中
|
||||
|
||||
this.$set(this.model, this.field, this.assignee)
|
||||
this.$set(this.model, "assigneeText", this.assigneeText)
|
||||
this.$set(this.model, "assigneeMode", this.assigneeMode)
|
||||
},
|
||||
searchUsers() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.loadUsers()
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
this.pageForm.pageSize = val
|
||||
this.loadUsers()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.loadUsers()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
var buttonConfig = {
|
||||
template: /*language=HTML*/ `
|
||||
<el-form-item label="按钮配置">
|
||||
{{model}}
|
||||
<el-table :data="model['buttonConfig']" style="width: 100%" border>
|
||||
<!-- <el-table-column prop="value" label="值" width="120">-->
|
||||
<!-- <template slot-scope="{ row }">-->
|
||||
<!-- <el-input v-model="row.value" disabled></el-input>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column prop="originalText" label="原始文本" width="100"></el-table-column>
|
||||
<el-table-column prop="replacementText" label="替换文本">
|
||||
<template slot-scope="{ row }">
|
||||
<el-input v-model="row.replacementText" :disabled="row.disabled"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="theme" label="主题">
|
||||
<template slot-scope="{ row }">
|
||||
<el-select v-model="row.theme" placeholder="请选择" :disabled="row.disabled">
|
||||
<el-option label="primary" value="primary"></el-option>
|
||||
<el-option label="success" value="success"></el-option>
|
||||
<el-option label="warning" value="warning"></el-option>
|
||||
<el-option label="danger" value="danger"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="icon" label="图标" width="100">
|
||||
<template slot-scope="{ row }">
|
||||
<el-input v-model="row.icon" placeholder="请输入图标名称" :disabled="row.disabled"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="visible" label="是否可见" width="100">
|
||||
<template slot-scope="{ row }">
|
||||
<el-switch v-model="row.visible" :disabled="row.disabled"></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
`,
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
// field: {
|
||||
// type: String,
|
||||
// required: true
|
||||
// }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
button: {},
|
||||
defaultConfig: [
|
||||
{ value: 0, originalText: "发起申请" },
|
||||
{ value: 1, originalText: "同意申请" },
|
||||
{ value: 2, originalText: "拒绝申请" },
|
||||
{ value: 3, originalText: "退回上一步" },
|
||||
{ value: 4, originalText: "跳转" },
|
||||
{ value: 5, originalText: "重新提交" },
|
||||
{ value: 6, originalText: "退回发起人" },
|
||||
{ value: 20, originalText: "会签拒绝申请" }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"model.buttonConfig": {
|
||||
handler(val) {
|
||||
this.$set(this.model, "buttonConfig", val)
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
"model.performType": {
|
||||
handler(val) {
|
||||
if (val === "ALL") {
|
||||
// 会签只有同意不同意
|
||||
this.$set(this.model, "buttonConfig", [
|
||||
{ value: 1, originalText: "同意", visible: true, disabled: true },
|
||||
{ value: 20, originalText: "不同意", visible: true, disabled: true }
|
||||
])
|
||||
} else {
|
||||
// 普通任务
|
||||
// const visibleConfigs = this.model.buttonConfig.filter((item) => item.visible)
|
||||
// if (visibleConfigs.length) {
|
||||
// this.$set(this.model, "buttonConfig", visibleConfigs)
|
||||
// } else {
|
||||
// this.$set(this.model, "buttonConfig", [
|
||||
// { value: 1, originalText: "同意", visible: true, disabled: false },
|
||||
// { value: 2, originalText: "拒绝", visible: true, disabled: false },
|
||||
// { value: 3, originalText: "退回上一步", visible: true, disabled: false },
|
||||
// { value: 4, originalText: "跳转", visible: true, disabled: false },
|
||||
// { value: 6, originalText: "退回发起人", visible: true, disabled: false }
|
||||
// ])
|
||||
// }
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
"model.name": {
|
||||
handler(val) {
|
||||
if (val === "startTask") {
|
||||
// 申请只有发起申请
|
||||
this.$set(this.model, "buttonConfig", [{ value: 0, originalText: "提交", visible: true, disabled: false }])
|
||||
}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
if (this.model["buttonConfig"] === undefined) {
|
||||
this.$set(this.model, "buttonConfig", this.defaultConfig)
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||
<title>流程设计器</title>
|
||||
<script src="/assets/platform/plugins/vue/vue.js"></script>
|
||||
<script src="/assets/platform/plugins/jquery/jquery.js"></script>
|
||||
<script src="/assets/platform/plugins/element-ui/lib/index.js"></script>
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index_custom.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/override.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/css/root.css" />
|
||||
<!-- 引入 core 包和对应 css-->
|
||||
<script src="/assets/platform/plugins/logicflow/logic-flow.js"></script>
|
||||
<link rel="stylesheet" href="/assets/platform/plugins/logicflow/index.css" />
|
||||
<!-- <script src="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/logic-flow.js"></script>-->
|
||||
<!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@logicflow/core@1.2.12/dist/style/index.css" />-->
|
||||
<script src="/assets/platform/plugins/snaker/SnakerflowDesigner.umd.js"></script>
|
||||
<style>
|
||||
#app {
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<snaker-flow-designer
|
||||
ref="designer"
|
||||
v-model="flowData"
|
||||
@on-save="handleSave"
|
||||
:show-doc="false"
|
||||
node-render-type="html"
|
||||
:extend-property-keys="[
|
||||
'candidateUsers',
|
||||
'candidateGroups',
|
||||
'candidateHandler',
|
||||
'assigneeText',
|
||||
'assigneeMode',
|
||||
'buttonConfig',
|
||||
'enableNextOperator',
|
||||
'h5form'
|
||||
]"
|
||||
>
|
||||
<template v-slot:form-item-task-form="{model,field}">
|
||||
<el-form-item label="pc跳转地址">
|
||||
<el-input placeholder="请输入pc跳转地址" clearable v-model="model[field]"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="h5跳转地址">
|
||||
<el-input placeholder="请输入h5跳转地址" clearable v-model="model['h5form']"></el-input>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-slot:form-item-task-assignee="{model,field}">
|
||||
<assignee-form :model="model" :field="field" :start_node_next_node_ids="startNodeNextNodeIds"></assignee-form>
|
||||
</template>
|
||||
|
||||
<template v-slot:form-item-task-assignment-handler="{model,field}">
|
||||
<el-form-item label="参与者处理类">
|
||||
<el-select v-model="model[field]" style="width: 100%" clearable :disabled="startNodeNextNodeIds.includes(model.name)">
|
||||
<el-option
|
||||
v-for="item in assignmentHandlerClassOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="item.name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-slot:form-item-task-candidate-handler="{model,field}">
|
||||
<el-form-item label="候选者处理类">
|
||||
<el-select v-model="model[field]" style="width: 100%" clearable :disabled="startNodeNextNodeIds.includes(model.name)">
|
||||
<el-option
|
||||
v-for="item in candidateHandlerClassOptions"
|
||||
:key="item.value"
|
||||
:value="item.value"
|
||||
:label="item.name"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<template v-slot="{model,field}">
|
||||
<el-form-item label="指定下一步处理人">
|
||||
<el-checkbox v-model="model['enableNextOperator']">指定下一步处理人</el-checkbox>
|
||||
</el-form-item>
|
||||
|
||||
<button-config :model="model" :field="field"></button-config>
|
||||
</template>
|
||||
|
||||
<!-- <template v-slot="{model,field}">-->
|
||||
<!-- <el-form-item label="表单查看URL">-->
|
||||
<!-- <el-input v-model="model[instanceViewUrl]"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- </template>-->
|
||||
</snaker-flow-designer>
|
||||
</div>
|
||||
</body>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include('assigneeForm.js'){}#-->
|
||||
<!--#include('buttonConfig.js'){}#-->
|
||||
|
||||
Vue.use(SnakerflowDesigner.default)
|
||||
const vue = new Vue({
|
||||
el: "#app",
|
||||
components: {
|
||||
"assignee-form": assigneeForm,
|
||||
"button-config": buttonConfig
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: "${id!}",
|
||||
designerData: {},
|
||||
flowData: {},
|
||||
assignmentHandlerClassOptions: [],
|
||||
candidateHandlerClassOptions: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
startNodeNextNodeIds() {
|
||||
if (this.$refs.designer && this.$refs.designer.lf) {
|
||||
const graphData = this.$refs.designer.lf.getGraphData()
|
||||
if (graphData) {
|
||||
const { nodes, edges } = graphData
|
||||
const startNode = nodes.find((v) => v.type === "snaker:start")
|
||||
if (startNode) {
|
||||
const targetNodeIds = edges.filter((v) => v.sourceNodeId === startNode.id).map((v) => v.targetNodeId)
|
||||
return targetNodeIds
|
||||
}
|
||||
}
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSave(val) {
|
||||
const design = {
|
||||
...this.designerData,
|
||||
content: val.json
|
||||
}
|
||||
$.post("/flow/design/updateContent", { design: JSON.stringify(design) }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
window.parent.postMessage("success")
|
||||
} else {
|
||||
window.parent.postMessage("error")
|
||||
}
|
||||
})
|
||||
},
|
||||
getDetail() {
|
||||
$.get("/flow/design/detail", { id: this.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.designerData = res.data
|
||||
if (res.data.content) {
|
||||
this.flowData = res.data.content
|
||||
} else {
|
||||
this.flowData = {}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
init() {
|
||||
$.get("/flow/design/assigmentHandlerClass").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.assignmentHandlerClassOptions = res.data
|
||||
}
|
||||
})
|
||||
$.get("/flow/design/candidateHandlerClass").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.candidateHandlerClassOptions = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getData() {
|
||||
console.log(this.$refs.designer)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
mounted() {
|
||||
if (this.id) {
|
||||
this.getDetail()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,279 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.design-dialog .el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="流程名称">
|
||||
<el-input v-model="pageForm.displayName" placeholder="请输入流程名称" clearable></el-input>
|
||||
</search-item>
|
||||
<search-item label="流程编码">
|
||||
<el-input v-model="pageForm.name" placeholder="请输入流程编码" clearable></el-input>
|
||||
</search-item>
|
||||
<search-item label="流程分类">
|
||||
<el-select v-model="pageForm.category" placeholder="请选择流程分类" style="width: 100%"
|
||||
@change="doSearch" clearable>
|
||||
<el-option v-for="item in categoryOptions" :label="item.name" :value="item.id" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="是否部署">
|
||||
<el-select v-model="pageForm.deployed" placeholder="请选择是否部署" style="width: 100%"
|
||||
@change="doSearch" clearable>
|
||||
<el-option label="全部" :value="null"></el-option>
|
||||
<el-option label="已部署" :value="true"></el-option>
|
||||
<el-option label="未部署" :value="false"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button type="primary" @click="onAdd" size="small" icon="el-icon-plus">新增</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" @sort-change="pageOrder" style="width: 100%">
|
||||
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="displayName" label="流程定义名称"></el-table-column>
|
||||
<el-table-column prop="name" label="流程定义编码"></el-table-column>
|
||||
<el-table-column prop="category" label="流程分类" sortable="custom">
|
||||
<template slot-scope="{row}">
|
||||
{{categoryOptions.find(v=>v.id === row.category)?.name}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="isDeployed" label="是否部署">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag size="mini" v-if="row.isDeployed===1" type="success">是</el-tag>
|
||||
<el-tag size="mini" v-else-if="row.isDeployed===0" type="info">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序编码" prop="sortNo" width="120" align="center" header-align="center" sortable="custom">
|
||||
<template slot-scope="{row}">
|
||||
<el-input-number
|
||||
v-model="row.sortNo"
|
||||
:controls="false"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
size="mini"
|
||||
style="width: 88px"
|
||||
@change="updateSortNo(row)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" fixed="right" width="450px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="onDesign(row)">设计</el-button>
|
||||
</el-button>
|
||||
<el-button size="mini" type="primary" @click="onEdit(row)">编辑</el-button>
|
||||
<!-- v-if="row.isDeployed===0"-->
|
||||
<el-button type="primary" size="mini" @click="onDeploy(row)">部署
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" @click="onDelete(row)">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogVisible" width="70%">
|
||||
<el-form :model="formData" ref="formRef" label-width="120px">
|
||||
<el-form-item label="流程定义名称" prop="displayName"
|
||||
:rules="{ required: true, message: '请输入流程定义名称', trigger: 'blur' }">
|
||||
<el-input v-model="formData.displayName" placeholder="请输入流程定义名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程定义编码" prop="name"
|
||||
:rules="{ required: true, message: '请输入流程定义编码', trigger: 'blur' }">
|
||||
<el-input v-model="formData.name" placeholder="请输入流程定义编码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程分类" prop="category"
|
||||
:rules="{ required: false, message: '请选择流程分类', trigger: 'change' }"
|
||||
style="width: 100%">
|
||||
<el-select v-model="formData.category" placeholder="请选择流程分类" clearable style="width: 100%">
|
||||
<el-option v-for="item in categoryOptions" :key="item.id" :label="item.name"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="电脑端发起地址" prop="instanceUrl">
|
||||
<el-input v-model="formData.instanceUrl" placeholder="请输入电脑端发起地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机端发起地址" prop="h5InstanceUrl">
|
||||
<el-input v-model="formData.h5InstanceUrl" placeholder="请输入手机端发起地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="电脑端查看地址" prop="instanceViewUrl">
|
||||
<el-input v-model="formData.instanceViewUrl" placeholder="请输入电脑端发起地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机端查看地址" prop="h5InstanceViewUrl">
|
||||
<el-input v-model="formData.h5InstanceViewUrl" placeholder="请输入手机端发起地址"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="picIcon" label="图片图标">
|
||||
<file-upload
|
||||
:value.sync="formData.picIcon"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<el-input v-model="formData.icon" placeholder="请输入图标"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="说明" prop="description">
|
||||
<text-editor v-model="formData.description"></text-editor>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="流程设计" :visible.sync="designVisible" fullscreen class="design-dialog">
|
||||
<div style="height: calc(100vh - 55px); width: 100%">
|
||||
<iframe v-if="designVisible" :src="designUrl" frameborder="0" height="100%"
|
||||
style="height: 100%; width: inherit"></iframe>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
categoryOptions: [],
|
||||
designVisible: false,
|
||||
designUrl: null,
|
||||
formData: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onAdd() {
|
||||
this.formData = {}
|
||||
this.dialogVisible = true
|
||||
},
|
||||
onEdit(row) {
|
||||
this.dialogVisible = true
|
||||
this.formData = { ...row }
|
||||
},
|
||||
onSubmit() {
|
||||
$.post("/flow/design/" + (this.formData.id ? "update" : "insert"), { design: JSON.stringify(this.formData) }).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
this.dialogVisible = false
|
||||
}
|
||||
})
|
||||
},
|
||||
onDesign(row) {
|
||||
this.designVisible = true
|
||||
this.iframeLoaded()
|
||||
this.designUrl = "/flow/design/designer?id=" + row.id
|
||||
},
|
||||
|
||||
iframeLoaded() {
|
||||
// iframe监听组件内设计器保存事件
|
||||
window.onmessage = (event) => {
|
||||
console.log(event)
|
||||
if (event.data === "success") {
|
||||
this.$message.success("保存成功")
|
||||
this.designVisible = false
|
||||
this.pageData()
|
||||
} else if (event.data === "error") {
|
||||
this.$message.error("保存失败")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
onDeploy(row) {
|
||||
this.$confirm("部署会生成新的流程定义版本,确定部署吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/design/deploy", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onRedeploy(row) {
|
||||
this.$confirm("重新部署会覆盖最新版流程定义,确定重新部署吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/design/redeploy", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onDelete(row) {
|
||||
this.$confirm("您确定要删除吗", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/flow/design/delete", { id: row.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
onView(row) {
|
||||
},
|
||||
|
||||
listCategory() {
|
||||
this.$axios.post("/flow/category/list").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.categoryOptions = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
updateSortNo(row) {
|
||||
const sortNo = Number(row.sortNo)
|
||||
if (!Number.isInteger(sortNo) || sortNo < 0) {
|
||||
this.$message.error("排序编码必须是非负整数")
|
||||
return
|
||||
}
|
||||
$.post("/flow/design/updateSortNo", {
|
||||
id: row.id,
|
||||
sortNo
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.listCategory()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,70 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-row type="flex">
|
||||
<el-button @click="openDesigner" size="medium" type="primary">
|
||||
<i class="ti-plus"></i>
|
||||
设计流程
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%">
|
||||
<el-table-column label="模板ID" prop="id"></el-table-column>
|
||||
<el-table-column label="流程模版名称" prop="name"></el-table-column>
|
||||
<el-table-column label="关键字" prop="key"></el-table-column>
|
||||
<el-table-column label="版本" prop="version"></el-table-column>
|
||||
<el-table-column label="创建日期" prop="createDate"></el-table-column>
|
||||
<el-table-column label="操作" width="200px">
|
||||
<template scope="scope">
|
||||
<el-button size="mini" type="primary">查看</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
methods: {
|
||||
pageData() {
|
||||
$.get(loc() + "/pageData", this.pageForm).then((res) => {
|
||||
console.log(res)
|
||||
this.tableData = res.data.result
|
||||
this.pageForm.totalCount = res.data.total
|
||||
})
|
||||
},
|
||||
|
||||
doDelete(processId) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
$.post(loc() + "/deleteProcess", { processId }).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
openDesigner() {
|
||||
window.open("/uflo/designer")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,218 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<!-- <iframe src="/uflo/designer"></iframe>-->
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-row type="flex">
|
||||
<el-button @click="openDesigner" size="medium" type="primary">
|
||||
<i class="ti-plus"></i>
|
||||
设计流程
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%">
|
||||
<el-table-column label="主题" prop="subject"></el-table-column>
|
||||
<el-table-column label="标签" prop="tag"></el-table-column>
|
||||
<el-table-column label="流程定义ID" prop="processId"></el-table-column>
|
||||
<el-table-column label="流程实例ID" prop="id"></el-table-column>
|
||||
<el-table-column label="业务ID" prop="businessId"></el-table-column>
|
||||
<el-table-column label="创建日期" prop="createDate"></el-table-column>
|
||||
<el-table-column label="所在节点" prop="currentNode"></el-table-column>
|
||||
<el-table-column label="发起人" prop="promoter"></el-table-column>
|
||||
<el-table-column label="操作" width="200px">
|
||||
<template scope="scope">
|
||||
<el-button size="mini" type="primary" @click="loadTask(scope.row.id)">任务列表</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDeleteProcessInstance(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="任务列表" width="1000px" :visible.sync="taskDialog">
|
||||
<el-table :data="taskList" border>
|
||||
<el-table-column label="任务ID" prop="id"></el-table-column>
|
||||
<el-table-column label="任务名称" prop="taskName"></el-table-column>
|
||||
<el-table-column label="节点名称" prop="nodeName"></el-table-column>
|
||||
<el-table-column label="处理人" prop="owner"></el-table-column>
|
||||
<el-table-column label="类型" prop="type"></el-table-column>
|
||||
<el-table-column label="创建日期" prop="createDate"></el-table-column>
|
||||
<el-table-column label="状态" prop="state"></el-table-column>
|
||||
<el-table-column label="操作" width="200">
|
||||
<template scope="scope">
|
||||
<el-link type="primary" @click="openComplete(scope.row.id)">完成任务</el-link>
|
||||
<el-link type="primary">领取任务</el-link>
|
||||
<el-link type="primary" @click="openJump(scope.row.id)">跳转</el-link>
|
||||
<el-link type="primary">流程图</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="任务跳转" width="600px" :visible.sync="taskJumpDialog" :close-on-click-modal="false">
|
||||
<el-form :model="taskJumpFormData" ref="taskJumpForm" size="small" label-position="top">
|
||||
<el-form-item prop="nodeName" label="跳转目标节点" :rules="[{required:true,message:'请选择',trigger:['change','blur']}]">
|
||||
<el-select v-model="taskJumpFormData.nodeName" style="width: 100%">
|
||||
<el-option v-for="item in taskJumpNodeList" :key="item.name" :label="item.name" :value="item.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="taskJumpDialog = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doTaskJump">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="完成任务" width="600px" :visible.sync="taskCompleteDialog" :close-on-click-modal="false">
|
||||
<el-form :model="taskCompleteFormData" ref="taskCompleteForm" size="small" label-position="top">
|
||||
<el-form-item prop="variables" label="流程变量">
|
||||
<el-table :data="taskCompleteFormData.variables" border size="mini">
|
||||
<el-table-column label="变量名" prop="key"></el-table-column>
|
||||
<el-table-column label="变量名" prop="value"></el-table-column>
|
||||
<el-table-column label="操作">
|
||||
<template slot="header" scope="scope">
|
||||
<el-button type="primary" size="mini" @click="taskCompleteFormData.variables.push({})">添加变量</el-button>
|
||||
</template>
|
||||
<template slot="header" scope="scope">
|
||||
<el-button type="primary" size="mini" @click="taskCompleteFormData.variables.splice(scope.$index,1)">删除变量</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="sequenceFlowName" label="流转路径" :rules="[{required:true,message:'请选择',trigger:['change','blur']}]">
|
||||
<el-select v-model="taskCompleteFormData.sequenceFlowName" style="width: 100%">
|
||||
<el-option v-for="item in taskSequenceFlows" :key="item.name" :label="item.name" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="taskCompleteDialog = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doTaskComplete">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
mode: "running"
|
||||
},
|
||||
taskDialog: false,
|
||||
taskList: [],
|
||||
|
||||
processInstanceId: null,
|
||||
|
||||
taskJumpDialog: false,
|
||||
taskJumpNodeList: [],
|
||||
taskJumpFormData: {
|
||||
taskId: null,
|
||||
nodeName: null
|
||||
},
|
||||
|
||||
taskCompleteDialog: false,
|
||||
taskSequenceFlows: [],
|
||||
taskCompleteFormData: {
|
||||
variables: [],
|
||||
taskId: null,
|
||||
sequenceFlowName: null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
$.get(loc() + "/pageData", this.pageForm).then((res) => {
|
||||
console.log(res)
|
||||
this.tableData = res.data.result
|
||||
this.pageForm.totalCount = res.data.total
|
||||
})
|
||||
},
|
||||
doDeleteProcessInstance(processInstanceId) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
$.post(loc() + "/deleteProcessInstance", { processInstanceId }).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
loadTask(processInstanceId) {
|
||||
$.get(loc() + "/loadTask", { processInstanceId }).then((res) => {
|
||||
this.taskDialog = true
|
||||
this.taskList = res.data
|
||||
})
|
||||
},
|
||||
|
||||
openComplete(taskId) {
|
||||
this.taskCompleteDialog = true
|
||||
if (this.$refs.taskCompleteForm) {
|
||||
this.$refs.taskCompleteForm.resetFields()
|
||||
}
|
||||
$.get(loc() + "/loadSequenceFlows", { taskId }).then((res) => {
|
||||
this.taskSequenceFlows = res.data
|
||||
this.taskSequenceFlows.unshift({
|
||||
name: "默认",
|
||||
value: null
|
||||
})
|
||||
this.taskCompleteFormData.taskId = taskId
|
||||
})
|
||||
},
|
||||
doTaskComplete() {
|
||||
this.$confirm("真的要完成这个任务吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
$.post(loc() + "/completeTask", this.taskCompleteFormData).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.taskCompleteDialog = false
|
||||
this.taskDialog = false
|
||||
this.pageData()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
openJump(taskId) {
|
||||
this.taskJumpDialog = true
|
||||
if (this.$refs.taskJumpForm) {
|
||||
this.$refs.taskJumpForm.resetFields()
|
||||
}
|
||||
$.get(loc() + "/loadJumpTasks", { taskId }).then((res) => {
|
||||
this.taskJumpNodeList = res.data
|
||||
this.taskJumpFormData.taskId = taskId
|
||||
})
|
||||
},
|
||||
doTaskJump() {
|
||||
$.post(loc() + "/jumpTask", this.taskJumpFormData).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.taskJumpDialog = false
|
||||
this.taskDialog = false
|
||||
this.pageData()
|
||||
})
|
||||
},
|
||||
|
||||
openDesigner() {
|
||||
window.open("/uflo/designer")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,489 @@
|
||||
<!--#
|
||||
layout("/layouts/v4/baseLayout.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.task-todo-center {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 统计卡片样式 */
|
||||
.statistics-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
border: none;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.stat-card .el-card__body {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 20px;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.todo-icon {
|
||||
background-color: rgba(64, 158, 255, 0.1);
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.done-icon {
|
||||
background-color: rgba(103, 194, 58, 0.1);
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.started-icon {
|
||||
background-color: rgba(230, 162, 60, 0.1);
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.stat-info {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
/* 搜索区域样式 */
|
||||
.filter-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.filter-section .el-card__body {
|
||||
padding: 15px 20px;
|
||||
}
|
||||
|
||||
.search-form {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.search-form .el-form-item {
|
||||
margin-bottom: 0;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
/* 任务列表区域样式 */
|
||||
.task-section .el-card__header {
|
||||
padding: 0;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* 优化后的 tabs 样式 */
|
||||
.task-header .el-tabs__header {
|
||||
margin: 0;
|
||||
padding: 0 20px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.task-header .el-tabs__nav-wrap::after {
|
||||
height: 1px;
|
||||
background-color: #ebeef5;
|
||||
}
|
||||
|
||||
.task-header .el-tabs__item {
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 15px;
|
||||
color: #606266;
|
||||
padding: 0 20px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.task-header .el-tabs__item.is-active {
|
||||
color: #409eff;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.task-header .el-tabs__active-bar {
|
||||
height: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
/* 表格行样式 */
|
||||
.el-table .hover-row {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 优化后的分页样式 */
|
||||
.pagination-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination {
|
||||
padding: 0;
|
||||
font-weight: normal;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .btn-prev,
|
||||
.pagination-container .el-pagination .btn-next {
|
||||
background-color: #f4f6f8;
|
||||
color: #606266;
|
||||
border-radius: 4px;
|
||||
padding: 0 10px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .btn-prev:hover,
|
||||
.pagination-container .el-pagination .btn-next:hover {
|
||||
color: var(--color-primary);
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pager li {
|
||||
background-color: #f4f6f8;
|
||||
color: #606266;
|
||||
border-radius: 4px;
|
||||
min-width: 32px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 3px;
|
||||
font-weight: normal;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pager li:hover {
|
||||
color: var(--color-primary);
|
||||
background-color: #ecf5ff;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pager li.active {
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__jump {
|
||||
margin-left: 15px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__editor.el-input {
|
||||
width: 50px;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.pagination-container .el-pagination .el-pagination__editor.el-input .el-input__inner {
|
||||
height: 28px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div class="task-todo-center">
|
||||
<!-- 统计数据区域 -->
|
||||
<el-row :gutter="20" class="statistics-section">
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon todo-icon">
|
||||
<i class="el-icon-s-claim"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{todoCount}}</div>
|
||||
<div class="stat-label">待办任务</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon done-icon">
|
||||
<i class="el-icon-finished"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{doneCount}}</div>
|
||||
<div class="stat-label">已办任务</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-content">
|
||||
<div class="stat-icon started-icon">
|
||||
<i class="el-icon-s-promotion"></i>
|
||||
</div>
|
||||
<div class="stat-info">
|
||||
<div class="stat-value">{{startedCount}}</div>
|
||||
<div class="stat-label">我发起的</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 搜索筛选区域 -->
|
||||
<el-card shadow="never" class="filter-section">
|
||||
<el-form :inline="true" :model="pageForm" class="search-form">
|
||||
<el-form-item>
|
||||
<el-input v-model="pageForm.searchKeyword" placeholder="请输入关键词搜索"
|
||||
prefix-icon="el-icon-search"
|
||||
clearable></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="pageForm.category" placeholder="所有流程分类" clearable>
|
||||
<el-option v-for="type in categoryOptions" :key="type.id" :label="type.name"
|
||||
:value="type.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="search">搜索</el-button>
|
||||
<el-button @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
|
||||
<!-- 任务列表区域 -->
|
||||
<el-card shadow="never" class="task-section">
|
||||
<div slot="header" class="task-header">
|
||||
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
||||
<el-tab-pane label="待办任务" name="todo"></el-tab-pane>
|
||||
<el-tab-pane label="已办任务" name="done"></el-tab-pane>
|
||||
<el-tab-pane label="我发起的" name="started"></el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
<!-- 表格展示 -->
|
||||
<el-table v-loading="loading" :data="tasks" style="width: 100%" :key="activeTab"
|
||||
:header-cell-style="{backgroundColor: '#f5f7fa'}">
|
||||
<el-table-column prop="instanceName" label="流程名称" min-width="180" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="taskName" label="任务节点" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="categoryName" label="流程分类" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="initiatorName" label="申请人" min-width="120">
|
||||
<template slot-scope="{row}">{{row.variable.initiatorName}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-if="activeTab === 'started'" label="状态" width="100">
|
||||
<template slot-scope="{row}">{{processStatusMap[row.state]?.text}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100px" fixed="right">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" size="mini" @click="openView(scope.row)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 空状态展示 -->
|
||||
<el-empty v-if="tasks.length === 0" :description="getEmptyText()"></el-empty>
|
||||
|
||||
<!-- 分页 -->
|
||||
<div class="pagination-container" v-if="tasks.length > 0">
|
||||
<el-pagination
|
||||
@current-change="handleCurrentChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
// 统计数据
|
||||
todoCount: 0,
|
||||
doneCount: 0,
|
||||
startedCount: 0,
|
||||
|
||||
// 查询表单
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
todoCount: 0,
|
||||
keyword: "",
|
||||
category: ""
|
||||
},
|
||||
|
||||
|
||||
// 流程类型选项
|
||||
categoryOptions: [],
|
||||
|
||||
// 任务列表
|
||||
tasks: [],
|
||||
loading: false,
|
||||
|
||||
// 标签页
|
||||
activeTab: "todo",
|
||||
|
||||
// 当前任务
|
||||
currentTask: null,
|
||||
dialogVisible: false,
|
||||
|
||||
processStatusMap: {
|
||||
10: {text: "进行中", class: "doing"},
|
||||
20: {text: "已完成", class: "finished"},
|
||||
30: {text: "已撤回", class: "withdraw"},
|
||||
40: {text: "强行终止", class: "interrupt"},
|
||||
45: {text: "已拒绝", class: "reject"},
|
||||
50: {text: "挂起", class: "pending"},
|
||||
99: {text: "已废弃", class: "abandon"}
|
||||
},
|
||||
|
||||
// 任务状态枚举
|
||||
taskStateEnum: {
|
||||
DOING: 10,
|
||||
FINISHED: 20,
|
||||
WITHDRAW: 30,
|
||||
INTERRUPT: 40,
|
||||
PENDING: 50,
|
||||
ABANDON: 99
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
if(GetQueryString("status")){
|
||||
this.activeTab = GetQueryString("status")
|
||||
}
|
||||
this.initData()
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 初始化数据
|
||||
async initData() {
|
||||
await Promise.all([this.getStatistics(), this.listCategory(), this.getTasks()])
|
||||
},
|
||||
|
||||
// 获取统计数据
|
||||
async getStatistics() {
|
||||
try {
|
||||
const res = await $.post("/flow/todoCenter/statistics")
|
||||
if (res.code === 0) {
|
||||
const {todoCount, doneCount, startedCount} = res.data
|
||||
this.todoCount = todoCount
|
||||
this.doneCount = doneCount
|
||||
this.startedCount = startedCount
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error("获取统计数据失败")
|
||||
console.error("获取统计数据失败:", error)
|
||||
}
|
||||
},
|
||||
|
||||
// 获取流程类型
|
||||
async listCategory() {
|
||||
this.$axios.post("/flow/category/list").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.categoryOptions = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 获取任务列表
|
||||
async getTasks() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await $.post("/flow/todoCenter/" + this.activeTab, this.pageForm)
|
||||
if (res.code === 0) {
|
||||
this.tasks = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
this.dialogVisible = false
|
||||
}
|
||||
} catch (error) {
|
||||
this.$message.error("获取任务列表失败")
|
||||
console.error("获取任务列表失败:", error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 处理标签页点击
|
||||
handleTabClick(tab) {
|
||||
this.activeTab = tab.name
|
||||
this.pageForm.pageNumber = 1
|
||||
this.getTasks()
|
||||
},
|
||||
|
||||
// 搜索
|
||||
search() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.getTasks()
|
||||
this.getStatistics()
|
||||
},
|
||||
|
||||
// 重置搜索
|
||||
reset() {
|
||||
this.pageForm.searchKeyword = ""
|
||||
this.pageForm.category = ""
|
||||
this.search()
|
||||
},
|
||||
|
||||
// 处理页码变化
|
||||
handleCurrentChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.getTasks()
|
||||
},
|
||||
|
||||
// 处理任务
|
||||
async openView(task) {
|
||||
console.log(task)
|
||||
const {taskId, taskKey, taskState, instanceId, businessNo, formKey} = task
|
||||
if (!formKey) {
|
||||
this.$message.warning("当前流程没有配置地址")
|
||||
return
|
||||
}
|
||||
window.open(formKey + "?taskId=" + taskId + "bizId=" + businessNo + "taskKey=" + taskKey)
|
||||
},
|
||||
|
||||
// 获取空状态文本
|
||||
getEmptyText() {
|
||||
switch (this.activeTab) {
|
||||
case "todo":
|
||||
return "您当前没有需要办理的任务,辛苦了"
|
||||
case "done":
|
||||
return "您当前没有已办理的任务记录"
|
||||
case "started":
|
||||
return "您当前没有发起的流程"
|
||||
default:
|
||||
return "暂无数据"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<div class="ele-exception">
|
||||
<div class="ele-exception-img"><img alt="" src="/assets/platform/img/403.svg" style="width: 216px; height: 250px" /></div>
|
||||
<div class="ele-exception-content">
|
||||
<h1>403</h1>
|
||||
<p>抱歉, 你无权访问该页面</p>
|
||||
<div>
|
||||
<a href="#" onclick="event.preventDefault();" class="router-link-active">
|
||||
<button
|
||||
@click="$store.dispatch('pjaxRoute','/platform/home')"
|
||||
type="button"
|
||||
class="el-button el-button--primary el-button--medium"
|
||||
>
|
||||
<span>返回首页</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store: store
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#}#-->
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<div class="ele-exception">
|
||||
<div class="ele-exception-img"><img alt="" src="/assets/platform/img/404.svg" style="width: 216px; height: 250px" /></div>
|
||||
<div class="ele-exception-content">
|
||||
<h1>403</h1>
|
||||
<p>抱歉, 你访问的页面不存在</p>
|
||||
<div>
|
||||
<a href="#" onclick="event.preventDefault();" class="router-link-active">
|
||||
<button
|
||||
@click="$store.dispatch('pjaxRoute','/platform/home')"
|
||||
type="button"
|
||||
class="el-button el-button--primary el-button--medium"
|
||||
>
|
||||
<span>返回首页</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store: store
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#}#-->
|
||||
@@ -0,0 +1,31 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<div class="ele-exception">
|
||||
<div class="ele-exception-img"><img alt="" src="/assets/platform/img/500.svg" style="width: 216px; height: 250px" /></div>
|
||||
<div class="ele-exception-content">
|
||||
<h1>403</h1>
|
||||
<p>抱歉, 服务器出错了</p>
|
||||
<div>
|
||||
<a href="#" onclick="event.preventDefault();" class="router-link-active">
|
||||
<button
|
||||
@click="$store.dispatch('pjaxRoute','/platform/home')"
|
||||
type="button"
|
||||
class="el-button el-button--primary el-button--medium"
|
||||
>
|
||||
<span>返回首页</span>
|
||||
</button>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store: store
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#}#-->
|
||||
@@ -0,0 +1,263 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<div class="btn-group tool-button mt5">
|
||||
<el-button size="medium" @click="openAdd">
|
||||
<i class="ti-plus"></i>
|
||||
新建密钥
|
||||
</el-button>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%">
|
||||
<el-table-column sortable prop="name" label="应用名称" header-align="center" align="center" width="120"></el-table-column>
|
||||
<el-table-column prop="appid" label="App Id" header-align="center" width="160"></el-table-column>
|
||||
<el-table-column prop="appkey" label="App Key" header-align="center" width="320">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="isVisable(scope.row.appid)" class="bg-success">{{scope.row.appkey}}</span>
|
||||
<a
|
||||
v-if="!isVisable(scope.row.appid)"
|
||||
@click="visiableData.push(scope.row.appid)"
|
||||
style="padding-left: 5px; color: #06c; cursor: pointer"
|
||||
>
|
||||
显示
|
||||
</a>
|
||||
<a
|
||||
v-if="isVisable(scope.row.appid)"
|
||||
@click="setNotVisable(scope.row.appid)"
|
||||
style="padding-left: 5px; color: #06c; cursor: pointer"
|
||||
>
|
||||
隐藏
|
||||
</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column sortable prop="createdAt" label="创建时间" header-align="center" align="center">
|
||||
<template slot-scope="scope">{{formatAt(scope.row.createdAt)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column sortable prop="disabled" label="启用状态" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="scope.row.disabled" class="fa fa-circle text-danger ml5"></i>
|
||||
<i v-if="!scope.row.disabled" class="fa fa-circle text-success ml5"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'enable',appid:scope.row.appid,name:scope.row.name,row:scope.row}">
|
||||
启用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',appid:scope.row.appid,name:scope.row.name,row:scope.row}">
|
||||
禁用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided :command="{type:'delete',appid:scope.row.appid,name:scope.row.name}">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
<el-dialog title="新建密钥" :visible.sync="addDialogVisible" width="50%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="name" label="应用名称">
|
||||
<el-input maxlength="100" placeholder="应用名称" v-model="formData.name" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
visiableData: [],
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
formData: {
|
||||
id: ""
|
||||
},
|
||||
formRules: {
|
||||
name: [{ required: true, message: "应用名称", trigger: "blur" }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isVisable: function (appid) {
|
||||
if (this.visiableData.indexOf(appid) >= 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
setNotVisable: function (appid) {
|
||||
var index = this.visiableData.indexOf(appid)
|
||||
if (index >= 0) {
|
||||
this.visiableData.splice(index, 1)
|
||||
}
|
||||
},
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/api/data",
|
||||
self.pageForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list
|
||||
self.pageForm.totalCount = data.data.totalCount
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
formatAt: function (val) {
|
||||
if (val && val > 0) return moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
},
|
||||
doSearch: function () {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
openAdd: function () {
|
||||
var self = this
|
||||
this.addDialogVisible = true
|
||||
this.formData = {} //打开新增窗口,表单先清空
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
doAdd: function () {
|
||||
var self = this
|
||||
self.$refs["addForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
base + "/platform/sys/api/addDo",
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.addDialogVisible = false
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
self.pageData() //添加失败也可以入库,所以加载一下看看
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
dropdownCommand: function (command) {
|
||||
//监听下拉框事件
|
||||
var self = this
|
||||
if ("enable" == command.type || "disable" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/api/" + command.type + "/" + command.appid,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
if ("disable" == command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" == command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("delete" == command.type) {
|
||||
self.$confirm("此操作将删除密钥 " + command.name, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/api/delete/" + command.appid,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.doSearch()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,517 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
/* 系统参数页面固定占满平台内容区,避免页面主体产生纵向滚动条。 */
|
||||
#app .sys-conf-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
height: calc(100vh - 82px);
|
||||
overflow: hidden;
|
||||
}
|
||||
#app .sys-conf-query-card {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
/* 列表卡片占用剩余空间,数据超出时仅由 Element 表格内部滚动。 */
|
||||
#app .sys-conf-list-card {
|
||||
flex: 1 1 auto;
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
#app .sys-conf-list-card > .el-card__body {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
#app .sys-conf-list-card .ele-table-tool,
|
||||
#app .sys-conf-list-card .el-pagination-container {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<div class="sys-conf-layout">
|
||||
<el-card class="sys-conf-query-card" shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="参数名">
|
||||
<el-input v-model="pageForm.configKey" clearable placeholder="请输入参数名"
|
||||
@keyup.enter.native="doSearch"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card class="mt10 sys-conf-list-card" shadow="never">
|
||||
<table-tool label="系统参数">
|
||||
<el-button @click="openAdd" size="small" type="primary" icon="el-icon-plus">新增</el-button>
|
||||
</table-tool>
|
||||
<el-table v-loading="tableLoading" :data="tableData" :height="tableHeight"
|
||||
@sort-change="pageOrder" header-align="center" ref="tableRef" style="width: 100%">
|
||||
<el-table-column header-align="center" label="参数名" prop="configKey" sortable
|
||||
width="200"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="参数值" prop="configValue"
|
||||
width="200"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="说明"
|
||||
prop="note"></el-table-column>
|
||||
<el-table-column label="操作" prop="note" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'edit',id:scope.row.configKey}">修改
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',id:scope.row.configKey}"
|
||||
v-if="scope.row.configKey.indexOf('App')<0">
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</div>
|
||||
</guava>
|
||||
|
||||
<el-dialog title="新增参数" :visible.sync="addDialogVisible" :close-on-click-modal="false" width="40%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" label-width="80px">
|
||||
<el-form-item prop="configKey" label="参数名">
|
||||
<el-input maxlength="100" placeholder="参数名" v-model="formData.configKey" auto-complete="off"
|
||||
tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="configValue" label="参数值">
|
||||
<el-input maxlength="100" placeholder="参数值" v-model="formData.configValue" auto-complete="off"
|
||||
tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="note" label="说 明">
|
||||
<el-input type="textarea" v-model="formData.note"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="修改参数" :visible.sync="editDialogVisible" :close-on-click-modal="false" width="40%">
|
||||
<el-form :model="formData" ref="editForm" :rules="formRules" label-width="80px">
|
||||
<el-form-item prop="configKey" label="参数名">
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="参数名"
|
||||
v-model="formData.configKey"
|
||||
auto-complete="off"
|
||||
tabindex="1"
|
||||
type="text"
|
||||
disabled
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="configValue" label="参数值">
|
||||
<template v-if="formData.configKey === 'AppLogo'">
|
||||
<file-upload
|
||||
key="AppLogo"
|
||||
style="--upload-width: 214px;--upload-height:64px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>最佳分辨率:428 x 128</span>
|
||||
</template>
|
||||
<template v-else-if="formData.configKey === 'AppHomeImg'">
|
||||
<file-upload
|
||||
key="AppHomeImg"
|
||||
style="--upload-width: 214px;--upload-height:64px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="5"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>PC 首页轮播图,最佳分辨率:2756 x 732,最多可上传 5 张,首页按上传顺序轮播</span>
|
||||
</template>
|
||||
<template v-else-if="formData.configKey === 'H5AppHomeImg'">
|
||||
<file-upload
|
||||
key="H5AppHomeImg"
|
||||
style="--upload-width: 214px;--upload-height:110px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="5"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>移动端首页轮播图,建议分辨率:750 x 386,最多可上传 5 张,首页按上传顺序轮播</span>
|
||||
</template>
|
||||
<template v-else-if="formData.configKey === 'PCHomeImg'">
|
||||
<file-upload
|
||||
key="PCHomeImg"
|
||||
style="--upload-width: 214px;--upload-height:64px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="10"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>最佳分辨率:2756 x 732,最多可上传 10 张,PC端首页按上传顺序轮播</span>
|
||||
</template>
|
||||
<template v-else-if="formData.configKey === 'AppFeaturedActivityImg'">
|
||||
<file-upload
|
||||
key="AppFeaturedActivityImg"
|
||||
style="--upload-width: 214px;--upload-height:96px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>最佳分辨率:750 x 280,精彩活动页顶部图片</span>
|
||||
</template>
|
||||
<template v-else-if="formData.configKey === 'AppFestivalBenefitImg'">
|
||||
<file-upload
|
||||
key="AppFestivalBenefitImg"
|
||||
style="--upload-width: 214px;--upload-height:96px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>最佳分辨率:750 x 280,节日福利页顶部图片</span>
|
||||
</template>
|
||||
<template v-else-if="formData.configKey === 'TeacherCongressCardBackImg'">
|
||||
<file-upload
|
||||
key="TeacherCongressCardBackImg"
|
||||
style="--upload-width: 214px;--upload-height:300px"
|
||||
:value.sync="formData.configValue"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
<span>教代会代表证背景图片,通过文件上传配置</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-if="'true'===formData.configValue||'false'===formData.configValue">
|
||||
<el-radio-group v-model="formData.configValue">
|
||||
<el-radio label="true">是</el-radio>
|
||||
<el-radio label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div v-else>
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="参数值"
|
||||
v-model="formData.configValue"
|
||||
auto-complete="off"
|
||||
tabindex="2"
|
||||
type="text"
|
||||
></el-input>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item prop="note" label="说 明">
|
||||
<el-input type="textarea" v-model="formData.note"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
tableLoading: false,
|
||||
tableHeight: 300,
|
||||
tableResizeObserver: null,
|
||||
pageRequestSequence: 0,
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
pageSize: 10,
|
||||
pageNumber: 1,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
totalCount: 0,
|
||||
configKey: ""
|
||||
},
|
||||
formData: {
|
||||
configKey: "",
|
||||
configValue: "",
|
||||
note: ""
|
||||
},
|
||||
formRules: {
|
||||
configKey: [{required: true, message: "请输入参数名", trigger: "blur"}],
|
||||
configValue: [{required: true, message: "请输入参数值", trigger: "blur"}]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doSearch: function () {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
openAdd: function () {
|
||||
this.addDialogVisible = true
|
||||
this.formData = {} //打开新增窗口,表单先清空
|
||||
},
|
||||
doAdd: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/conf/addDo"
|
||||
self.$refs["addForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
//表单验证通过
|
||||
$.post(
|
||||
url,
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.addDialogVisible = false
|
||||
self.pageData() //重新加载分页数据
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
doEdit: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/conf/editDo"
|
||||
self.normalizeConfigValue()
|
||||
self.$refs["editForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
url,
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.editDialogVisible = false
|
||||
self.pageData() //重新加载分页数据
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
normalizeConfigValue: function () {
|
||||
if (Array.isArray(this.formData.configValue)) {
|
||||
this.formData.configValue = this.formData.configValue
|
||||
.map(function (item) {
|
||||
if (typeof item === "string") {
|
||||
return item
|
||||
}
|
||||
if (item && item.response && item.response.data) {
|
||||
return item.response.data
|
||||
}
|
||||
if (item && item.url) {
|
||||
return item.url
|
||||
}
|
||||
if (item && item.data) {
|
||||
return item.data
|
||||
}
|
||||
return ""
|
||||
})
|
||||
.filter(function (item) {
|
||||
return item
|
||||
})
|
||||
.join(",")
|
||||
}
|
||||
if (typeof this.formData.configValue === "string") {
|
||||
this.formData.configValue = this.formData.configValue
|
||||
.split(",")
|
||||
.map(function (item) {
|
||||
return item.trim()
|
||||
})
|
||||
.filter(function (item) {
|
||||
return item
|
||||
})
|
||||
.join(",")
|
||||
}
|
||||
},
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
// 查询期间清空旧页并显示加载状态,快速连续查询时仅接收最后一次响应。
|
||||
var self = this
|
||||
var requestSequence = ++self.pageRequestSequence
|
||||
self.tableData = []
|
||||
self.tableLoading = true
|
||||
$.post(base + "/platform/sys/conf/data", self.pageForm).then(function (data) {
|
||||
if (requestSequence != self.pageRequestSequence) {
|
||||
return
|
||||
}
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list || []
|
||||
self.pageForm.totalCount = data.data.totalCount || 0
|
||||
self.updateTableHeight()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
}).always(function () {
|
||||
if (requestSequence == self.pageRequestSequence) {
|
||||
self.tableLoading = false
|
||||
}
|
||||
})
|
||||
},
|
||||
updateTableHeight: function () {
|
||||
var self = this
|
||||
this.$nextTick(function () {
|
||||
var cardBody = self.$el.querySelector(".sys-conf-list-card > .el-card__body")
|
||||
if (!cardBody || cardBody.clientHeight <= 0) {
|
||||
return
|
||||
}
|
||||
var getOuterHeight = function (element) {
|
||||
if (!element) {
|
||||
return 0
|
||||
}
|
||||
var style = window.getComputedStyle(element)
|
||||
return element.offsetHeight
|
||||
+ (parseFloat(style.marginTop) || 0)
|
||||
+ (parseFloat(style.marginBottom) || 0)
|
||||
}
|
||||
var bodyStyle = window.getComputedStyle(cardBody)
|
||||
var bodyPadding = (parseFloat(bodyStyle.paddingTop) || 0)
|
||||
+ (parseFloat(bodyStyle.paddingBottom) || 0)
|
||||
var tableTool = cardBody.querySelector(".ele-table-tool")
|
||||
var pagination = cardBody.querySelector(".el-pagination-container")
|
||||
var reservedHeight = bodyPadding + getOuterHeight(tableTool) + getOuterHeight(pagination)
|
||||
self.tableHeight = Math.max(1, Math.floor(cardBody.clientHeight - reservedHeight))
|
||||
self.$nextTick(function () {
|
||||
if (self.$refs.tableRef) {
|
||||
self.$refs.tableRef.doLayout()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
dropdownCommand: function (command) {
|
||||
//监听下拉框事件
|
||||
var self = this
|
||||
if ("edit" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/conf/edit/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.formData = data.data //加载后台表单数据
|
||||
self.editDialogVisible = true //打开编辑窗口
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("delete" == command.type) {
|
||||
self.$confirm("此操作将删除 " + command.id, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/conf/delete/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.pageData()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
},
|
||||
mounted: function () {
|
||||
var self = this
|
||||
this.updateTableHeight()
|
||||
if (window.ResizeObserver) {
|
||||
this.tableResizeObserver = new ResizeObserver(function () {
|
||||
self.updateTableHeight()
|
||||
})
|
||||
this.tableResizeObserver.observe(this.$el)
|
||||
} else {
|
||||
this.resizeHandler = function () {
|
||||
self.updateTableHeight()
|
||||
}
|
||||
window.addEventListener("resize", this.resizeHandler)
|
||||
}
|
||||
},
|
||||
beforeDestroy: function () {
|
||||
if (this.tableResizeObserver) {
|
||||
this.tableResizeObserver.disconnect()
|
||||
}
|
||||
if (this.resizeHandler) {
|
||||
window.removeEventListener("resize", this.resizeHandler)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,19 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<file-upload upload_mode="drag"></file-upload>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
created() {}
|
||||
})
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
||||
<!--# } #-->
|
||||
@@ -0,0 +1,634 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
#app {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
min-height: calc(100vh - 64px);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
|
||||
.box-card {
|
||||
width: calc(100% - 20px);
|
||||
min-height: calc(100vh - 84px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.box-card .el-card__body {
|
||||
min-height: calc(100vh - 170px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.box-card-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-height: calc(100vh - 170px);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.card-body-center {
|
||||
width: 60%;
|
||||
min-width: 800px;
|
||||
min-height: calc(100vh - 170px);
|
||||
position: relative;
|
||||
padding-bottom: 70px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.card-body-center-bottom {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-body-center-bottom button {
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.card-body-center > .el-carousel {
|
||||
height: calc(100vh - 300px);
|
||||
min-height: 560px;
|
||||
position: relative;
|
||||
overflow-y: hidden;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.card-body-center > .el-carousel > .el-carousel__container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.carousel-table-item {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 10px 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.carousel-table-item-center {
|
||||
height: 100%;
|
||||
width: 90%;
|
||||
min-width: 800px;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.carousel-table-item-center .el-card__body {
|
||||
flex: 1;
|
||||
height: auto;
|
||||
min-height: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.check-group {
|
||||
display: block;
|
||||
overflow: visible;
|
||||
padding: 0 10px 5px 0;
|
||||
height: auto;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.check-group .el-radio,
|
||||
.check-group .el-checkbox {
|
||||
display: block;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
margin: 0 !important;
|
||||
margin-top: 15px !important;
|
||||
}
|
||||
|
||||
.check-group .el-checkbox__label,
|
||||
.check-group .el-radio__label {
|
||||
width: calc(100% - 16px);
|
||||
}
|
||||
|
||||
.option-extra {
|
||||
float: right;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.carousel-table-item-relation .el-card__body,
|
||||
.carousel-table-item-form .el-card__body {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.carousel-table-item-form .el-card__body {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.result-card {
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
.result-card .el-card__body {
|
||||
min-height: 170px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.result-text {
|
||||
font-size: 12px;
|
||||
color: rgb(100, 100, 100);
|
||||
margin-top: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card class="box-card" shadow="never">
|
||||
<div slot="header" class="clearfix">
|
||||
<span class="box-card-title">DataKid</span>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="card-body-center">
|
||||
<el-steps :active="active" align-center>
|
||||
<el-step title="选择数据表" icon="el-icon-document-copy"></el-step>
|
||||
<el-step title="选择数据列" icon="el-icon-s-unfold"></el-step>
|
||||
<el-step title="数据列对应" icon="el-icon-s-operation"></el-step>
|
||||
<el-step title="提交数据" icon="el-icon-upload"></el-step>
|
||||
<el-step title="导入结果" icon="el-icon-bell"></el-step>
|
||||
</el-steps>
|
||||
|
||||
<el-carousel indicator-position="none" :autoplay="false" arrow="never" ref="carousel" direction="vertical">
|
||||
<el-carousel-item class="carousel-table-item">
|
||||
<el-card class="carousel-table-item-center">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-input
|
||||
style="width: 200px"
|
||||
v-model.trim="tableKeyword"
|
||||
placeholder="请输入关键字查询"
|
||||
clearable>
|
||||
</el-input>
|
||||
<el-button type="primary" icon="el-icon-search" @click="filterTables"></el-button>
|
||||
<el-button v-if="formData.tableName" style="float: right; padding: 10px 0" type="text">
|
||||
当前选中:{{ formData.tableName }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-radio-group class="check-group" v-model="formData.tableName">
|
||||
<el-radio
|
||||
v-for="item in filteredTables"
|
||||
:key="item.table_name"
|
||||
:label="item.table_name"
|
||||
border>
|
||||
{{ item.table_name }}{{ item.table_comment ? " ( " + item.table_comment + " )" : "" }}
|
||||
<span class="option-extra">rows: {{ item.table_rows || 0 }}</span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-card>
|
||||
</el-carousel-item>
|
||||
|
||||
<el-carousel-item class="carousel-table-item">
|
||||
<el-card class="carousel-table-item-center">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-input
|
||||
style="width: 200px"
|
||||
v-model.trim="columnKeyword"
|
||||
placeholder="请输入关键字查询"
|
||||
clearable>
|
||||
</el-input>
|
||||
<el-button type="primary" icon="el-icon-search" @click="filterColumns"></el-button>
|
||||
<el-button style="float: right; padding: 10px 0" type="text">
|
||||
{{ formData.tableName }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<el-checkbox-group class="check-group" v-model="formData.columns">
|
||||
<el-checkbox
|
||||
v-for="item in filteredColumns"
|
||||
:key="item.column_name"
|
||||
:label="item.column_name"
|
||||
border>
|
||||
{{ item.column_name }}{{ item.column_comment ? " ( " + item.column_comment + " )" : "" }}
|
||||
<span class="option-extra">{{ item.column_type }}</span>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-card>
|
||||
</el-carousel-item>
|
||||
|
||||
<el-carousel-item class="carousel-table-item">
|
||||
<el-card style="width: 100%" class="carousel-table-item-relation">
|
||||
<el-table :data="relation" height="100%" style="width: 100%" border size="medium">
|
||||
<el-table-column prop="column_name" label="列名"></el-table-column>
|
||||
<el-table-column prop="column_comment" label="描述" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="is_nullable" label="可为空"></el-table-column>
|
||||
<el-table-column prop="column_type" label="类型"></el-table-column>
|
||||
<el-table-column prop="column_key" label="key"></el-table-column>
|
||||
<el-table-column min-width="200px">
|
||||
<template slot="header">
|
||||
目标列
|
||||
<el-popover
|
||||
placement="top-start"
|
||||
title="提示"
|
||||
trigger="hover"
|
||||
content="Excel 对应的表头名称,用于和表数据列对应">
|
||||
<i slot="reference" class="el-icon-question"></i>
|
||||
</el-popover>
|
||||
</template>
|
||||
<template slot-scope="{ row }">
|
||||
<el-input
|
||||
v-model.trim="row.relation"
|
||||
size="medium"
|
||||
placeholder="目标列">
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-carousel-item>
|
||||
|
||||
<el-carousel-item class="carousel-table-item">
|
||||
<el-card style="width: 100%" class="carousel-table-item-form">
|
||||
<el-form label-width="120px" :model="formData" ref="form" :rules="formRules">
|
||||
<el-form-item label=" " label-width="135px" class="view-header"></el-form-item>
|
||||
|
||||
<el-form-item prop="method" label="导入方式">
|
||||
<el-radio-group v-model="formData.method">
|
||||
<el-radio-button :label="1">追加或更新</el-radio-button>
|
||||
<el-radio-button :label="2">仅追加</el-radio-button>
|
||||
<el-radio-button :label="3">仅更新</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item v-if="[1, 3].includes(formData.method)" prop="field" label="关键字段">
|
||||
<el-select v-model="formData.field" clearable filterable placeholder="请选择" style="width: 50%">
|
||||
<el-option
|
||||
v-for="item in relation"
|
||||
:key="item.column_name"
|
||||
:label="item.column_name"
|
||||
:value="item.column_name">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="plugins" label="可选插件">
|
||||
<el-checkbox-group v-model="formData.plugins">
|
||||
<el-checkbox
|
||||
v-for="item in plugins"
|
||||
:key="item.name"
|
||||
:label="item.name"
|
||||
border>
|
||||
{{ item.description }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="文  件" class="is-required">
|
||||
<el-upload
|
||||
ref="upload"
|
||||
drag
|
||||
action="#"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:file-list="fileList"
|
||||
:on-change="handleFileChange"
|
||||
:on-remove="handleFileRemove">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
||||
<div slot="tip" class="el-upload__tip" style="color: #F56C6C">只能上传 xls/xlsx 文件</div>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-carousel-item>
|
||||
|
||||
<el-carousel-item style="padding: 10px">
|
||||
<el-card class="result-card">
|
||||
<el-progress type="circle" :percentage="resultPercentage" :status="resultProgressStatus"></el-progress>
|
||||
<div class="result-text">{{ resultSummary }}</div>
|
||||
<div style="margin-top: 10px" v-if="result.cacheKey">
|
||||
<el-link
|
||||
type="danger"
|
||||
icon="el-icon-download"
|
||||
style="font-size: 13px;"
|
||||
:href="loc() + '/exportErrors?cacheKey=' + result.cacheKey">
|
||||
下载错误记录
|
||||
</el-link>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-carousel-item>
|
||||
</el-carousel>
|
||||
|
||||
<div class="card-body-center-bottom">
|
||||
<el-button size="medium" @click="prev" :disabled="active === 0 || loading">上一步</el-button>
|
||||
<el-button type="primary" size="medium" :loading="loading" @click="active < 4 ? next() : carry()">
|
||||
{{ active < 4 ? primaryButtonText : "完成" }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
active: 0,
|
||||
loading: false,
|
||||
tableKeyword: "",
|
||||
columnKeyword: "",
|
||||
tables: [],
|
||||
filteredTables: [],
|
||||
columns: [],
|
||||
filteredColumns: [],
|
||||
relation: [],
|
||||
plugins: [],
|
||||
fileList: [],
|
||||
cacheTableName: "",
|
||||
result: {
|
||||
total: 0,
|
||||
success: 0,
|
||||
cacheKey: ""
|
||||
},
|
||||
formRules: {
|
||||
method: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
field: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
},
|
||||
formData: {
|
||||
tableName: "",
|
||||
columns: [],
|
||||
method: 1,
|
||||
field: "",
|
||||
plugins: []
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
primaryButtonText() {
|
||||
if (this.active === 3 && this.formData.method === 3) {
|
||||
return "更新"
|
||||
}
|
||||
return "下一步"
|
||||
},
|
||||
resultSummary() {
|
||||
const total = this.result.total || 0
|
||||
const success = this.result.success || 0
|
||||
const fail = Math.max(total - success, 0)
|
||||
if (fail > 0) {
|
||||
return "总记录数 " + total + " 条,成功 " + success + " 条,失败 " + fail + " 条!"
|
||||
}
|
||||
return "总记录数 " + total + " 条,成功 " + success + " 条!"
|
||||
},
|
||||
resultProgressStatus() {
|
||||
const total = this.result.total || 0
|
||||
const success = this.result.success || 0
|
||||
if (total > 0 && success < total) {
|
||||
return "exception"
|
||||
}
|
||||
return "success"
|
||||
},
|
||||
resultPercentage() {
|
||||
if (!this.result.total) {
|
||||
return 0
|
||||
}
|
||||
return Number(((this.result.success / this.result.total) * 100).toFixed(2))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loc,
|
||||
normalizeRecord(item) {
|
||||
const normalized = {}
|
||||
Object.keys(item || {}).forEach(key => {
|
||||
normalized[(key || "").toLowerCase()] = item[key]
|
||||
})
|
||||
return normalized
|
||||
},
|
||||
async loadTables() {
|
||||
const resp = await this.$axios.post(loc() + "/tableNames")
|
||||
if (resp.code === 0) {
|
||||
this.tables = Array.isArray(resp.data)
|
||||
? resp.data.map(item => this.normalizeRecord(item))
|
||||
: []
|
||||
this.filteredTables = this.tables.slice()
|
||||
}
|
||||
},
|
||||
async loadPlugins() {
|
||||
this.plugins = await this.$businessTool.getEnumOptions("SysDataImportPlugin")
|
||||
},
|
||||
async loadColumns() {
|
||||
const resp = await this.$axios.post(loc() + "/columns", {
|
||||
tableName: this.formData.tableName
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.columns = Array.isArray(resp.data)
|
||||
? resp.data.map(item => this.normalizeRecord(item))
|
||||
: []
|
||||
this.filteredColumns = this.columns.slice()
|
||||
}
|
||||
},
|
||||
filterTables() {
|
||||
const keyword = (this.tableKeyword || "").toLowerCase()
|
||||
if (!keyword) {
|
||||
this.filteredTables = this.tables.slice()
|
||||
return
|
||||
}
|
||||
this.filteredTables = this.tables.filter(item => {
|
||||
const tableName = (item.table_name || "").toLowerCase()
|
||||
const tableComment = (item.table_comment || "").toLowerCase()
|
||||
return tableName.includes(keyword) || tableComment.includes(keyword)
|
||||
})
|
||||
},
|
||||
filterColumns() {
|
||||
const keyword = (this.columnKeyword || "").toLowerCase()
|
||||
if (!keyword) {
|
||||
this.filteredColumns = this.columns.slice()
|
||||
return
|
||||
}
|
||||
this.filteredColumns = this.columns.filter(item => {
|
||||
const columnName = (item.column_name || "").toLowerCase()
|
||||
const columnComment = (item.column_comment || "").toLowerCase()
|
||||
return columnName.includes(keyword) || columnComment.includes(keyword)
|
||||
})
|
||||
},
|
||||
buildRelation() {
|
||||
this.relation = this.columns
|
||||
.filter(item => this.formData.columns.includes(item.column_name))
|
||||
.map(item => Object.assign({}, item, { relation: "" }))
|
||||
},
|
||||
async next() {
|
||||
if (this.active === 0) {
|
||||
if (!this.formData.tableName) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "请选择数据表",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.cacheTableName !== this.formData.tableName) {
|
||||
await this.loadColumns()
|
||||
this.formData.columns = []
|
||||
this.formData.field = ""
|
||||
this.cacheTableName = this.formData.tableName
|
||||
}
|
||||
} else if (this.active === 1) {
|
||||
if (!this.formData.columns.length) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "请选择数据列",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
this.buildRelation()
|
||||
} else if (this.active === 2) {
|
||||
if (this.relation.find(item => !item.relation)) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "存在未填写的目标列",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
} else if (this.active === 3) {
|
||||
const success = await this.doSubmit()
|
||||
if (!success) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
this.$refs.carousel.next()
|
||||
this.active++
|
||||
},
|
||||
prev() {
|
||||
if (this.active <= 0) {
|
||||
return
|
||||
}
|
||||
this.$refs.carousel.prev()
|
||||
this.active--
|
||||
},
|
||||
validateSubmitForm() {
|
||||
let validForm = true
|
||||
this.$refs.form.validate(valid => {
|
||||
validForm = valid
|
||||
})
|
||||
if (!validForm || !this.fileList.length) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "存在未填写的必填项",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
async doSubmit() {
|
||||
if (!this.validateSubmitForm()) {
|
||||
return false
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append("tableName", this.formData.tableName)
|
||||
formData.append("method", this.formData.method)
|
||||
formData.append("field", this.formData.field || "")
|
||||
formData.append("relation", JSON.stringify(this.relation))
|
||||
formData.append("plugins", JSON.stringify(this.formData.plugins || []))
|
||||
formData.append("file", this.fileList[0].raw, this.fileList[0].name)
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const resp = await this.$axios.post(loc() + "/importData", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
|
||||
if (resp.code !== 0) {
|
||||
this.$notify.error({
|
||||
title: "错误",
|
||||
message: resp.msg
|
||||
})
|
||||
this.result = { total: 0, success: 0, cacheKey: "" }
|
||||
return false
|
||||
}
|
||||
|
||||
this.result = resp.data || { total: 0, success: 0, cacheKey: "" }
|
||||
return true
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
handleFileRemove(file, fileList) {
|
||||
this.fileList = fileList
|
||||
},
|
||||
handleFileChange(file, fileList) {
|
||||
const extension = ((file.name || "").split(".").pop() || "").toLowerCase()
|
||||
const remove = () => {
|
||||
const removeIndex = fileList.findIndex(item => item.uid === file.uid)
|
||||
if (removeIndex > -1) {
|
||||
fileList.splice(removeIndex, 1)
|
||||
}
|
||||
}
|
||||
|
||||
if (file.size === 0) {
|
||||
this.$message.warning("您选择的是空文件")
|
||||
remove()
|
||||
} else if (!["xls", "xlsx"].includes(extension)) {
|
||||
this.$message.warning("上传文件只能是 xls/xlsx 格式")
|
||||
remove()
|
||||
}
|
||||
|
||||
this.fileList = fileList
|
||||
},
|
||||
carry() {
|
||||
this.active = 0
|
||||
this.tableKeyword = ""
|
||||
this.columnKeyword = ""
|
||||
this.columns = []
|
||||
this.filteredColumns = []
|
||||
this.relation = []
|
||||
this.fileList = []
|
||||
this.cacheTableName = ""
|
||||
this.result = {
|
||||
total: 0,
|
||||
success: 0,
|
||||
cacheKey: ""
|
||||
}
|
||||
this.formData = {
|
||||
tableName: "",
|
||||
columns: [],
|
||||
method: 1,
|
||||
field: "",
|
||||
plugins: []
|
||||
}
|
||||
if (this.$refs.upload) {
|
||||
this.$refs.upload.clearFiles()
|
||||
}
|
||||
this.filterTables()
|
||||
this.$refs.carousel.setActiveItem(0)
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadTables()
|
||||
await this.loadPlugins()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak></div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app"
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,211 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.pullTimeRadioGroup .el-checkbox {
|
||||
width: 100%;
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
|
||||
.pullTimeRadioGroup .el-checkbox.is-bordered {
|
||||
margin-left: 0 !important;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pullTimeRadioGroup .el-checkbox__input {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pullTimeRadioGroup .el-checkbox__label {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="拉取时间">
|
||||
<el-select clearable v-model="pageForm.pullTime" placeholder="请选择拉取时间" :clearable="false" @change="doSearch">
|
||||
<el-option v-for="item in pullTimeOptions" :label="item.pullTime" :value="item.pullTime"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="姓名">
|
||||
<el-input placeholder="请输入姓名" clearable v-model="pageForm.userName" style="width: 100%"></el-input>
|
||||
</search-item>
|
||||
<search-item label="工号">
|
||||
<el-input placeholder="请输入工号" clearable v-model="pageForm.loginName" style="width: 100%"></el-input>
|
||||
</search-item>
|
||||
<search-item label="所属单位">
|
||||
<el-select placeholder="请选择所属单位" v-model="pageForm.unitName" clearable filterable>
|
||||
<el-option v-for="item in unitNameOptions" :key="item" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="在职状态">
|
||||
<el-select v-model="pageForm.userState" clearable filterable>
|
||||
<el-option v-for="item in userStateOptions" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="用人方式">
|
||||
<el-select v-model="pageForm.preparedBy" clearable filterable>
|
||||
<el-option v-for="item in preparedByOptions" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="人员类型">
|
||||
<el-select v-model="pageForm.personType" clearable filterable>
|
||||
<el-option v-for="item in personTypeOptions" :label="item" :value="item"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool :label="pageForm.pullTime ? pageForm.pullTime + '数据' : '全部数据'">
|
||||
<el-button type="primary" size="mini" :loading="pullLoading" @click="pull" icon="el-icon-download">拉取信息中心数据</el-button>
|
||||
<el-button type="danger" size="mini" @click="openDelete" icon="el-icon-delete">删除本地数据源</el-button>
|
||||
</table-tool>
|
||||
<el-table :key="tableKey" :data="tableData" @sort-change="pageOrder" header-align="center" v-loading="tableLoading">
|
||||
<el-table-column type="index" width="70" label="序号" fixed="left" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="pullTime" label="拉取时间" width="170" fixed="left"></el-table-column>
|
||||
<el-table-column prop="loginname" label="工号" width="100" fixed="left"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" fixed="left" width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" sortable></el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" width="120"></el-table-column>
|
||||
<el-table-column prop="birthday" label="生日" sortable width="120">
|
||||
<template slot-scope="{row}">{{$moment(row.birthday).format('YYYY-MM-DD')}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userState" label="在职状态" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="arrivalAtSchoolDate" label="来校时间" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="personType" label="人员类型" sortable width="120"></el-table-column>
|
||||
<!-- <el-table-column prop="technicalTitle" label="技术职称" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="position" label="干部职务" sortable width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="education" label="学历" show-overflow-tooltip sortable width="120"></el-table-column>
|
||||
<el-table-column prop="academicDegree" label="学位" show-overflow-tooltip sortable width="120"></el-table-column>-->
|
||||
<el-table-column prop="unitName" label="单位" show-overflow-tooltip sortable width="120"></el-table-column>
|
||||
<el-table-column prop="unitId" label="单位编码" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="political" label="政治面貌" sortable></el-table-column>
|
||||
<el-table-column prop="nation" label="民族" sortable></el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="选择数据源" :visible.sync="pullTimeDialogVisible" width="30%">
|
||||
<el-checkbox-group v-model="sourceTime" style="width: 100%" class="pullTimeRadioGroup">
|
||||
<el-checkbox :label="item.pullTime" border v-for="item in pullTimeOptions" :key="item.pullTime">
|
||||
<div style="display: flex; justify-content: space-between">
|
||||
{{item.pullTime}}
|
||||
<span style="color: red">rows:{{item.num}}</span>
|
||||
</div>
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="pullTimeDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doDeleteUser">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
unitNameOptions: [],
|
||||
userStateOptions: [],
|
||||
preparedByOptions: [],
|
||||
personTypeOptions: [],
|
||||
pullTimeOptions: false,
|
||||
sourceTime: [],
|
||||
pullTimeDialogVisible: false,
|
||||
pullLoading: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pull() {
|
||||
this.$confirm("确定要拉取数据源吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
})
|
||||
.then(() => {
|
||||
this.pullLoading = true
|
||||
this.$axios
|
||||
.post("/platform/sys/data/user/pull/pullData")
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.getPullTimeOptions()
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.pullLoading = false
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
|
||||
getSearchOptions() {
|
||||
this.$axios.post("/platform/sys/data/user/pull/searchOptions").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.unitNameOptions = resp.data.unitNames
|
||||
this.userStateOptions = resp.data.userStates
|
||||
this.personTypeOptions = resp.data.personTypes
|
||||
this.preparedByOptions = resp.data.preparedBys
|
||||
}
|
||||
})
|
||||
},
|
||||
getPullTimeOptions() {
|
||||
this.$axios.post("/platform/sys/data/user/pull/pullTimeOptions").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.pullTimeOptions = resp.data
|
||||
if (this.pullTimeOptions.length > 0) {
|
||||
this.pageForm.pullTime = this.pullTimeOptions[0].pullTime
|
||||
this.pageData()
|
||||
} else {
|
||||
this.tableData = []
|
||||
this.pageForm.totalCount = 0
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
openDelete() {
|
||||
this.pullTimeDialogVisible = true
|
||||
this.getPullTimeOptions()
|
||||
},
|
||||
|
||||
doDeleteUser() {
|
||||
this.$confirm("确定要删除数据源吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios
|
||||
.post("/platform/sys/data/user/pull/deleteByPullTime", {
|
||||
pullTime: JSON.stringify(this.sourceTime)
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pullTimeDialogVisible = false
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// this.pageData()
|
||||
this.getSearchOptions()
|
||||
this.getPullTimeOptions()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.pullTimeRadioGroup .el-radio {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pullTimeRadioGroup .el-radio.is-bordered + .el-radio.is-bordered {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.condition-builder {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="姓名">
|
||||
<el-input placeholder="请输入姓名" clearable v-model="pageForm.userName" style="width: 100%"></el-input>
|
||||
</search-item>
|
||||
<search-item label="工号">
|
||||
<el-input placeholder="请输入工号" clearable v-model="pageForm.loginName" style="width: 100%"></el-input>
|
||||
</search-item>
|
||||
<search-item label="所属单位">
|
||||
<el-select placeholder="请选择所属单位" v-model="pageForm.unitId" clearable style="width: 100%">
|
||||
<el-option v-for="i in unitOptions" :label="i.name" :key="i.id" :value="i.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="在职状态">
|
||||
<dict-select v-model="pageForm.userState" code="USER_STATE" style="width: 100%"></dict-select>
|
||||
</search-item>
|
||||
<search-item label="人员类型">
|
||||
<dict-select v-model="pageForm.personType" code="USER_PERSON_TYPE" style="width: 100%"></dict-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool label="系统当前用户数据">
|
||||
<!--<el-button type="primary" size="mini" @click="updateDataDict" icon="el-icon-refresh">更新【码表】数据</el-button>-->
|
||||
<el-button type="primary" size="mini" @click="updateUnits" icon="el-icon-refresh">更新【单位】数据</el-button>
|
||||
<el-button type="primary" size="mini" @click="openUpdate" icon="el-icon-refresh">更新【人员】数据</el-button>
|
||||
</table-tool>
|
||||
<el-table :key="tableKey" :data="tableData" @sort-change="pageOrder" header-align="center">
|
||||
<el-table-column type="index" width="70" label="序号" fixed="left"></el-table-column>
|
||||
<el-table-column prop="loginname" label="工号" width="100" fixed="left"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" fixed="left" width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="changeTypes" label="变更类型" width="140">
|
||||
<template slot-scope="{row}">
|
||||
<span style="color: #0e78c5; cursor: pointer" @click="openCurrentUserChangeInfo(row)">
|
||||
{{getChangeTypes(row.changeTypes)}}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="changeTime" label="变更时间" sortable width="150"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" sortable></el-table-column>
|
||||
<!-- <el-table-column prop="mobile" label="手机号" width="120"></el-table-column>-->
|
||||
<el-table-column prop="birthday" label="生日" sortable width="120">
|
||||
<template slot-scope="{row}">{{$moment(row.birthday).format('YYYY-MM-DD')}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="userState" label="在职状态" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="personType" label="人员类型" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="arrivalAtSchoolDate" label="来校年月" sortable width="120"></el-table-column>
|
||||
<!--<el-table-column prop="technicalTitle" label="技术职称" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="position" label="职务" sortable width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="education" label="学历" show-overflow-tooltip sortable width="120"></el-table-column>
|
||||
<el-table-column prop="academicDegree" label="学位" show-overflow-tooltip sortable width="120"></el-table-column>-->
|
||||
<el-table-column prop="unitName" label="单位" show-overflow-tooltip sortable width="120"></el-table-column>
|
||||
<el-table-column prop="unitId" label="单位编码" sortable width="120"></el-table-column>
|
||||
<el-table-column prop="political" label="政治面貌" sortable></el-table-column>
|
||||
<el-table-column prop="nation" label="民族" sortable></el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #view>
|
||||
<member-all-change-info ref="memberAllChangeInfoRef"></member-all-change-info>
|
||||
</template>
|
||||
|
||||
</guava>
|
||||
<el-dialog title="选择数据源" :visible.sync="updateDialog" width="70%">
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="数据源" placement="top">
|
||||
<el-radio-group v-model="updateFromData.pullTime" style="width: 100%" class="pullTimeRadioGroup">
|
||||
<el-radio :label="item.pullTime" border v-for="item in pullTimeOptions" :key="item.pullTime">
|
||||
<span>
|
||||
{{item.pullTime}}
|
||||
<span style="float: right; color: red">rows:{{item.num}}</span>
|
||||
</span>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-timeline-item>
|
||||
|
||||
<el-timeline-item timestamp="更新方式" placement="top">
|
||||
<el-radio-group class="checkGroup" style="width: 100%" v-model="updateFromData.updateMode" size="small">
|
||||
<el-row>
|
||||
<el-radio border label="ALL">全部更新</el-radio>
|
||||
<el-radio border label="INCR">仅更新新增人员</el-radio>
|
||||
</el-row>
|
||||
</el-radio-group>
|
||||
</el-timeline-item>
|
||||
|
||||
<el-timeline-item timestamp="高级条件筛选" placement="top">
|
||||
<el-switch v-model="enableAdvancedConditions" active-text="启用高级条件"></el-switch>
|
||||
|
||||
<div v-if="enableAdvancedConditions" class="condition-builder">
|
||||
<!-- 条件构建器组件 -->
|
||||
<condition-group :group="updateFromData.conditionGroup" :field_options="fieldOptions" @remove="removeRootGroup"></condition-group>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="updateDialog = false" :disabled="updateLoading">取 消</el-button>
|
||||
<el-button type="primary" @click="execUpdate" :disabled="updateLoading" :loading="updateLoading">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("../../../zhgh/staffmanage/member/change/common/memberAllChangeInfo.js"){}#-->
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
updateDialog: false,
|
||||
pullTimeOptions: false,
|
||||
updateFromData: {
|
||||
pullTime: null,
|
||||
updateMode: "ALL",
|
||||
conditionGroup: {
|
||||
logic: "AND",
|
||||
conditions: [],
|
||||
groups: []
|
||||
}
|
||||
},
|
||||
updateLoading: false,
|
||||
unitOptions: [],
|
||||
changeTypeData: [],
|
||||
enableAdvancedConditions: false,
|
||||
// 可选字段列表
|
||||
fieldOptions: [
|
||||
{ label: "姓名", value: "username" },
|
||||
{ label: "工号", value: "loginname" },
|
||||
{ label: "性别", value: "sex" },
|
||||
{ label: "生日", value: "birthday" },
|
||||
{ label: "手机号", value: "mobile" },
|
||||
{ label: "在职状态", value: "userState" },
|
||||
{ label: "进站时间", value: "postDoctoralJoinDate" },
|
||||
{ label: "人员类型", value: "personType" },
|
||||
{ label: "来校年月", value: "arrivalAtSchoolDate" },
|
||||
{ label: "单位", value: "unitName" },
|
||||
{ label: "单位编码", value: "unitId" },
|
||||
{ label: "学历", value: "education" },
|
||||
{ label: "学位", value: "academicDegree" }
|
||||
]
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"member-all-change-info": MEMBER_ALL_CHANGE_INFO
|
||||
},
|
||||
methods: {
|
||||
// 更新单位数据
|
||||
updateUnits() {
|
||||
this.$confirm("确定要更新单位数据吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/sys/data/user/update/updateUnits")
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success("更新成功")
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
},
|
||||
// 更新码表数据
|
||||
updateDataDict() {
|
||||
this.$confirm("确定要更新码表数据吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/sys/data/user/update/updateDataDict")
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success("更新成功")
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
openCurrentUserChangeInfo(row) {
|
||||
this.$refs.guava.view(() => {
|
||||
this.$refs.memberAllChangeInfoRef.onOpen(row.id)
|
||||
})
|
||||
},
|
||||
getChangeTypes(changeTypes) {
|
||||
if (!changeTypes) return null
|
||||
const changeTypesList = JSON.parse(changeTypes)
|
||||
if (changeTypesList) {
|
||||
if (!changeTypesList || changeTypesList.length === 0 || !this.changeTypeData || this.changeTypeData.length === 0) return null
|
||||
return changeTypesList
|
||||
.map((v) => {
|
||||
return this.changeTypeData.find((item) => item.code === v).changeTypeName
|
||||
})
|
||||
.join(",")
|
||||
}
|
||||
return null
|
||||
},
|
||||
openUpdate() {
|
||||
this.updateDialog = true
|
||||
this.updateLoading = false
|
||||
this.$nextTick(() => {
|
||||
this.updateFromData = {
|
||||
updateMode: "ALL",
|
||||
pullTime: null,
|
||||
conditionGroup: {
|
||||
logic: "AND",
|
||||
conditions: [],
|
||||
groups: []
|
||||
}
|
||||
}
|
||||
this.enableAdvancedConditions = false
|
||||
})
|
||||
},
|
||||
getPullTimeOptions() {
|
||||
this.$axios.post("/platform/sys/data/user/update/pullTimeOptions").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.pullTimeOptions = resp.data
|
||||
}
|
||||
})
|
||||
},
|
||||
execUpdate() {
|
||||
if (!this.updateFromData.pullTime) {
|
||||
this.$message.warning("请选择数据源时间")
|
||||
return
|
||||
}
|
||||
if (!this.updateFromData.updateMode) {
|
||||
this.$message.warning("请选择更新方式")
|
||||
return
|
||||
}
|
||||
|
||||
// 如果未启用高级条件,则移除条件组
|
||||
if (!this.enableAdvancedConditions) {
|
||||
this.updateFromData.conditionGroup = null
|
||||
}
|
||||
|
||||
this.$confirm("确定要更新数据吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
this.updateLoading = true
|
||||
this.$axios
|
||||
.post("/platform/sys/data/user/update/updateUserFormSource", { param: JSON.stringify(this.updateFromData) })
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.updateDialog = false
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.updateLoading = false
|
||||
})
|
||||
})
|
||||
},
|
||||
listUnit() {
|
||||
this.$businessTool.listUnit(this.pageForm.unionId).then((res) => {
|
||||
this.unitOptions = res
|
||||
})
|
||||
},
|
||||
removeRootGroup() {
|
||||
// 重置根条件组
|
||||
this.updateFromData.conditionGroup = {
|
||||
logic: "AND",
|
||||
conditions: [],
|
||||
groups: []
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.listUnit()
|
||||
this.getPullTimeOptions()
|
||||
this.pageData()
|
||||
this.changeTypeData = await this.$businessTool.getEnumOptions("MemberChangeType")
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
|
||||
@@ -0,0 +1,478 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-button size="medium" @click="openAdd">
|
||||
<i class="ti-plus"></i>
|
||||
新建字典
|
||||
</el-button>
|
||||
<el-button size="medium" @click="openSort">排序</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table
|
||||
:key="tableKey"
|
||||
:data="tableData"
|
||||
style="width: 100%"
|
||||
:highlight-current-row="true"
|
||||
row-key="id"
|
||||
lazy
|
||||
:expand-row-keys="expandedRowKeysRenew"
|
||||
@expand-change="handleExpandChange"
|
||||
:load="loadChild"
|
||||
>
|
||||
<el-table-column
|
||||
label="字典名称"
|
||||
header-align="center"
|
||||
prop="name"
|
||||
width="200"
|
||||
:show-overflow-tooltip="true"
|
||||
align="left"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column label="字典代码" header-align="center" prop="code" :show-overflow-tooltip="true"></el-table-column>
|
||||
|
||||
<el-table-column label="启用状态" header-align="center" prop="disabled" align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="scope.row.disabled" class="fa fa-circle text-danger ml5"></i>
|
||||
<i v-if="!scope.row.disabled" class="fa fa-circle text-success ml5"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" header-align="center" prop="website" :show-overflow-tooltip="true" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'add',id:scope.row.id,name:scope.row.name}">添加子级</el-dropdown-item>
|
||||
<el-dropdown-item divided :command="{type:'enable',id:scope.row.id,name:scope.row.name,row:scope.row}">
|
||||
启用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',id:scope.row.id,name:scope.row.name,row:scope.row}">禁用</el-dropdown-item>
|
||||
<el-dropdown-item divided :command="{type:'edit',id:scope.row.id,name:scope.row.name}">修改</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',id:scope.row.id,name:scope.row.name}">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="新建字典" :visible.sync="addDialogVisible" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="parentId" label="上级字典" label-width="80px">
|
||||
<el-cascader
|
||||
v-if="!isAddFromSub"
|
||||
:props="props"
|
||||
:options="menuOptions"
|
||||
v-model="parentMenu"
|
||||
placeholder="不选择则为顶级"
|
||||
style="width: 100%"
|
||||
></el-cascader>
|
||||
<el-input v-if="isAddFromSub" type="text" v-model="formData.parentName" :disabled="true"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="字典名称">
|
||||
<el-input maxlength="100" placeholder="字典名称" v-model="formData.name" auto-complete="off" tabindex="3" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" label="字典代码">
|
||||
<el-input maxlength="100" placeholder="字典代码" v-model="formData.code" auto-complete="off" tabindex="5" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="字典备注">
|
||||
<el-input maxlength="100" placeholder="字典备注" v-model="formData.remark" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="修改字典" :visible.sync="editDialogVisible" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="editForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="name" label="字典名称">
|
||||
<el-input maxlength="100" placeholder="字典名称" v-model="formData.name" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code" label="字典代码">
|
||||
<el-input maxlength="100" placeholder="字典代码" v-model="formData.code" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="remark" label="字典备注">
|
||||
<el-input maxlength="100" placeholder="字典备注" v-model="formData.remark" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="字典排序" :visible.sync="sortDialogVisible" width="50%">
|
||||
<el-tree ref="sortMenuTree" :data="sortMenuData" draggable :allow-drop="sortAllowDrop" node-key="id" :props="defaultProps">
|
||||
<span class="custom-tree-node" slot-scope="scope">
|
||||
<span>{{ scope.node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="sortDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSort">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
sortDialogVisible: false,
|
||||
isAddFromSub: false,
|
||||
tableData: [], //后台取出的数据
|
||||
tableKey: "",
|
||||
formData: {
|
||||
id: "",
|
||||
parentId: "",
|
||||
parentName: "",
|
||||
type: "menu",
|
||||
target: "data-pjax",
|
||||
children: "false"
|
||||
},
|
||||
formRules: {
|
||||
name: [{ required: true, message: "字典名称", trigger: "blur" }],
|
||||
code: [{ required: true, message: "字典代码", trigger: ["blur", "change"] }]
|
||||
},
|
||||
menuOptions: [],
|
||||
props: {
|
||||
lazy: true,
|
||||
checkStrictly: true,
|
||||
multiple: false,
|
||||
lazyLoad: function (node, resolve) {
|
||||
var url = base + "/platform/sys/dict/tree"
|
||||
$.post(
|
||||
url,
|
||||
{ pid: node.value },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
resolve(data.data)
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
},
|
||||
parentMenu: [],
|
||||
sortMenuData: [],
|
||||
defaultProps: {
|
||||
children: "children",
|
||||
label: "label"
|
||||
},
|
||||
// 保存展开状态的数组
|
||||
expandedRowKeysRenew: [],
|
||||
tableTreeRefreshTool: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//排序树加载
|
||||
sortMenuLoad: function () {
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/dict/menuAll",
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.sortMenuData = data.data
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
//排序树控制不可跨级拖拽
|
||||
sortAllowDrop: function (moveNode, inNode, type) {
|
||||
if (moveNode.data.parentId == inNode.data.parentId) {
|
||||
return type == "prev"
|
||||
}
|
||||
},
|
||||
//打开排序功能
|
||||
openSort: function () {
|
||||
this.sortDialogVisible = true
|
||||
this.sortMenuLoad()
|
||||
},
|
||||
//递归获取所有的ID值
|
||||
getTreeIds: function (ids, data) {
|
||||
var self = this
|
||||
data.forEach(function (obj) {
|
||||
ids.push(obj.id)
|
||||
if (obj.children && obj.children.length > 0) {
|
||||
self.getTreeIds(ids, obj.children)
|
||||
}
|
||||
})
|
||||
},
|
||||
//提交排序后的数据
|
||||
doSort: function () {
|
||||
var self = this
|
||||
var ids = []
|
||||
self.getTreeIds(ids, self.sortMenuData)
|
||||
$.post(
|
||||
base + "/platform/sys/dict/sortDo",
|
||||
{ ids: ids.toString() },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.sortDialogVisible = false
|
||||
self.initTreeTable()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
openAdd: function () {
|
||||
var self = this
|
||||
self.isAddFromSub = false
|
||||
self.addDialogVisible = true
|
||||
self.parentMenu = []
|
||||
self.menuOptions = []
|
||||
//表单还原为初始值
|
||||
self.formData = {
|
||||
id: "",
|
||||
parentId: "",
|
||||
parentName: "",
|
||||
type: "menu",
|
||||
target: "data-pjax",
|
||||
children: "false"
|
||||
}
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
doAdd: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/dict/addDo"
|
||||
self.$refs["addForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
//表单验证通过
|
||||
if (!self.isAddFromSub) {
|
||||
//如果不是添加子菜单,则从级联选择框取父节点ID
|
||||
self.formData.parentId = self.parentMenu[self.parentMenu.length - 1] || ""
|
||||
}
|
||||
$.post(
|
||||
url,
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.addDialogVisible = false
|
||||
self.loadChildByExpandedKeys()
|
||||
// self.initTreeTable();
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
doEdit: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/dict/editDo"
|
||||
self.$refs["editForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
//表单验证通过
|
||||
$.post(
|
||||
url,
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.editDialogVisible = false
|
||||
self.loadChildByExpandedKeys()
|
||||
// self.initTreeTable()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
initTreeTable: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/dict/child"
|
||||
$.post(
|
||||
url,
|
||||
{ pid: "" },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data
|
||||
self.tableKey = +new Date()
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
loadChild: function (tree, treeNode, resolve) {
|
||||
// 在之前声明的全局变量中,增加一个key为 本条数据的id,id可替换为你数据中的任意唯一值
|
||||
this.tableTreeRefreshTool[tree.id] = {}
|
||||
// 重要!保存resolve方法,以便后续使用
|
||||
this.tableTreeRefreshTool[tree.id].resolve = resolve
|
||||
// 记录展开次数,具体作用后续介绍
|
||||
this.tableTreeRefreshTool[tree.id].expandCount = 0
|
||||
var url = base + "/platform/sys/dict/child"
|
||||
$.post(
|
||||
url,
|
||||
{ pid: tree.id },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
resolve(data.data)
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
dropdownCommand: function (command) {
|
||||
//监听下拉框事件
|
||||
var self = this
|
||||
if ("add" == command.type) {
|
||||
self.openAdd()
|
||||
self.formData.parentId = command.id
|
||||
self.formData.parentName = command.name
|
||||
self.isAddFromSub = true
|
||||
}
|
||||
if ("edit" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/dict/edit/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.formData = data.data //加载后台表单数据
|
||||
self.editDialogVisible = true //打开编辑窗口
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("enable" == command.type || "disable" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/dict/" + command.type + "/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
if ("disable" == command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" == command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("delete" == command.type) {
|
||||
self.$confirm("此操作将删除 " + command.name, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/dict/delete/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.loadChildByExpandedKeys()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
handleExpandChange(row, expanded) {
|
||||
if ((row.parentId === "" || row.parentId == null) && !expanded) {
|
||||
this.expandedRowKeysRenew = []
|
||||
return
|
||||
}
|
||||
if (expanded) {
|
||||
this.expandedRowKeysRenew.push(row.id)
|
||||
} else {
|
||||
const index = this.expandedRowKeysRenew.findIndex((v) => v === row.id)
|
||||
this.expandedRowKeysRenew.splice(index, 1)
|
||||
}
|
||||
},
|
||||
loadChildByExpandedKeys() {
|
||||
if (this.expandedRowKeysRenew && this.expandedRowKeysRenew.length > 0) {
|
||||
this.expandedRowKeysRenew.forEach((v) => {
|
||||
const curr = this.tableTreeRefreshTool[v]
|
||||
// api请求
|
||||
$.get("/platform/sys/dict/child", { pid: v }).then((res) => {
|
||||
curr.resolve(res.data)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.initTreeTable()
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.initTreeTable()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,126 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.record-img {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
overflow-clip-margin: content-box;
|
||||
overflow: clip;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="文件名称" prop="name">
|
||||
<el-input v-model="pageForm.name" placeholder="请输入文件名称" clearable></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button size="mini" type="primary" @click="openUploadFile">上传文件</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" header-align="center" border style="width: 100%">
|
||||
<el-table-column label="文件名称" prop="name"></el-table-column>
|
||||
<el-table-column label="缩略图" prop="thumbnail" width="100px">
|
||||
<template scope="{row}">
|
||||
<el-image
|
||||
v-if="row.suffix === 'jpg' || row.suffix === 'png' || row.suffix === 'row' || row.suffix === 'gif'"
|
||||
:src="row.thumbnail"
|
||||
:fit="'cover'"
|
||||
:style="{ width: '50px', height: '50px' }"
|
||||
></el-image>
|
||||
<el-image
|
||||
v-else
|
||||
:src="$commonUtil.getFileItemShowIcon(row.suffix)"
|
||||
:fit="'cover'"
|
||||
:style="{ width: '50px', height: '50px' }"
|
||||
></el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件大小" prop="sizeInfo" width="100px"></el-table-column>
|
||||
<el-table-column label="文件后缀" prop="suffix" width="100px"></el-table-column>
|
||||
<el-table-column label="储存引擎" prop="engine" width="100px"></el-table-column>
|
||||
<el-table-column label="操作" width="400px">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="primary" @click="$commonUtil.previewFile(scope.row)">预览</el-button>
|
||||
<el-button size="mini" type="primary" @click="openDetailFile(scope.row)">详情</el-button>
|
||||
<el-button size="mini" type="primary" @click="$downLoad(scope.row.downloadPath)">下载</el-button>
|
||||
<el-button size="mini" type="danger" @click="deleteFile(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
<el-dialog title="上传文件" width="50%" :visible.sync="uploadFileDialogVisible">
|
||||
<el-form :model="formData" ref="form">
|
||||
<el-form-item label="文件" :rules="[{ required: true, message: '请上传文件', trigger: ['blur','change'] }]">
|
||||
<file-upload :value.sync="formData.files" :upload_number="1" upload_result_category="interval" complete_result></file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog title="文件详情" :visible.sync="detailFileDialogVisible" width="50%">
|
||||
<el-descriptions :column="3" border>
|
||||
<el-descriptions-item label="文件名称" :span="3">{{viewData.name}}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件大小" :span="3">{{viewData.sizeInfo}}</el-descriptions-item>
|
||||
<el-descriptions-item label="文件后缀" :span="3">{{viewData.suffix}}</el-descriptions-item>
|
||||
<el-descriptions-item label="储存引擎" :span="3">{{viewData.bucket}}</el-descriptions-item>
|
||||
<el-descriptions-item label="储存路径" :span="3">{{viewData.storagePath}}</el-descriptions-item>
|
||||
<el-descriptions-item label="下载地址" :span="3">{{viewData.downloadPath}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
uploadFileDialogVisible: false,
|
||||
formData: {},
|
||||
detailFileDialogVisible: false,
|
||||
viewData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
deleteFile(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
$.post("/platform/sys/file/delete", {
|
||||
ids: JSON.stringify([id])
|
||||
}).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
})
|
||||
})
|
||||
},
|
||||
openUploadFile() {
|
||||
this.uploadFileDialogVisible = true
|
||||
this.formData = {}
|
||||
},
|
||||
openDetailFile(row) {
|
||||
console.log(row)
|
||||
this.detailFileDialogVisible = true
|
||||
this.viewData = { ...row }
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,525 @@
|
||||
<!doctype html>
|
||||
<html lang="${lang,escape}">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1" />
|
||||
<meta name="description" content="${AppName!}" />
|
||||
<title>${AppName!}</title>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- import CSS -->
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/fonts/themify-icons.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/css/common.css" />
|
||||
</head>
|
||||
<body>
|
||||
<!--[if lt IE 9]>
|
||||
<div class="ie-must-go-die">
|
||||
<div class="ie-container">
|
||||
<h1>您正在IE的世界中</h1>
|
||||
<p>本系统不支持IE9以下版本浏览器访问。</p>
|
||||
<a href="https://browsehappy.com" target="_blank">您可以点击此处,下载Chrome/Firefox后再次访问本页面。</a>
|
||||
</div>
|
||||
</div>
|
||||
<![endif]-->
|
||||
<div id="app" v-cloak>
|
||||
<div class="login-container">
|
||||
<div class="login-border">
|
||||
<el-row type="flex">
|
||||
<el-col :span="12" class="login-left">
|
||||
<img src="${base!}/assets/platform/img/banner.png" style="height: 485px" />
|
||||
</el-col>
|
||||
|
||||
<el-col>
|
||||
<div class="login-main">
|
||||
<h4 class="title">${AppName!}系统登录</h4>
|
||||
<el-tabs v-model="activeName" style="max-height: 275px">
|
||||
<el-tab-pane label="用户登录" name="signin">
|
||||
<span slot="label">
|
||||
<i slot="prefix" class>
|
||||
<i class="el-icon-user"></i>
|
||||
</i>
|
||||
用户登录
|
||||
</span>
|
||||
<el-form
|
||||
ref="loginForm"
|
||||
:model="loginForm"
|
||||
:rules="loginRules"
|
||||
class="login-form"
|
||||
auto-complete="on"
|
||||
label-position="left"
|
||||
>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
type="text"
|
||||
auto-complete="off"
|
||||
placeholder="账号或手机号码"
|
||||
prefix-icon="el-icon-user"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
auto-complete="off"
|
||||
placeholder="密码"
|
||||
@keyup.enter.native="handleLogin"
|
||||
prefix-icon="el-icon-lock"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="platformCaptcha">
|
||||
<el-input
|
||||
v-model="loginForm.platformCaptcha"
|
||||
auto-complete="off"
|
||||
placeholder="验证码"
|
||||
style="width: 60%"
|
||||
@keyup.enter.native="handleLogin"
|
||||
prefix-icon="el-icon-key"
|
||||
></el-input>
|
||||
<div class="login-code">
|
||||
<img :src="codeUrl" @click="getCode" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%">
|
||||
<el-button
|
||||
:loading="loading"
|
||||
size="medium"
|
||||
type="primary"
|
||||
style="width: 100%"
|
||||
@click.native.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="用户注册" name="signup">
|
||||
<span slot="label">
|
||||
<i slot="prefix" class>
|
||||
<i class="el-icon-mobile-phone"></i>
|
||||
</i>
|
||||
用户注册
|
||||
</span>
|
||||
|
||||
<el-form ref="regForm" class="login-form" status-icon :rules="regRules" :model="regForm"
|
||||
label-width="0">
|
||||
<el-form-item prop="mobile">
|
||||
<el-input
|
||||
v-model="regForm.mobile"
|
||||
auto-complete="off"
|
||||
placeholder="请输入手机号码"
|
||||
@keyup.enter.native="handleReg"
|
||||
prefix-icon="el-icon-mobile-phone"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="regForm.password"
|
||||
:type="passwordType"
|
||||
auto-complete="off"
|
||||
placeholder="请输入账号密码"
|
||||
@keyup.enter.native="handleReg"
|
||||
prefix-icon="el-icon-lock"
|
||||
>
|
||||
<i class="ti-eye" slot="suffix" @click="showPwd"></i>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="code">
|
||||
<el-input
|
||||
v-model="regForm.code"
|
||||
auto-complete="off"
|
||||
placeholder="请输入验证码"
|
||||
@keyup.enter.native="handleSend"
|
||||
prefix-icon="el-icon-key"
|
||||
>
|
||||
<template slot="append">
|
||||
<span
|
||||
style="cursor: pointer"
|
||||
class="msg-text"
|
||||
:class="[{ display: msgKey }]"
|
||||
@click="handleSend"
|
||||
>
|
||||
{{ msgText }}
|
||||
</span>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button size="medium" type="primary" style="width: 100%"
|
||||
@click.native.prevent="handleReg">
|
||||
<span v-if="!regLoading">注 册</span>
|
||||
<span v-else>注 册 中...</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<div class="third-login">
|
||||
<el-divider>第三方登录</el-divider>
|
||||
<div class="third-way">
|
||||
<span class="third-icon" @click="handleSocial()">
|
||||
<img src="/assets/platform/img/svg/github.svg"
|
||||
style="width: 32px; height: 32px" />
|
||||
</span>
|
||||
<span class="third-icon" @click="handleSocial()">
|
||||
<img src="/assets/platform/img/svg/wechat.svg"
|
||||
style="width: 32px; height: 32px" />
|
||||
</span>
|
||||
<span class="third-icon" @click="handleSocial()">
|
||||
<img src="/assets/platform/img/svg/qq.svg"
|
||||
style="width: 32px; height: 32px" />
|
||||
</span>
|
||||
<span class="third-icon" @click="handleSocial()">
|
||||
<img src="/assets/platform/img/svg/alipay.svg"
|
||||
style="width: 32px; height: 32px" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div id="el-login-footer" class="login-footer">
|
||||
<span>
|
||||
<a href="https://BudWk.com" target="_blank">BudWk.com</a>
|
||||
©
|
||||
<span>{{year}}</span>
|
||||
</span>
|
||||
<span>⋅</span>
|
||||
<a href="https://beian.miit.gov.cn"
|
||||
target="_blank">${@config.getString("DomainBeian","皖ICP备15001937号-3")}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<!-- import Vue before Element -->
|
||||
<!--<script src="${base!}/assets/platform/plugins/vue/vue.js"></script>-->
|
||||
<script src="https://cdn.staticfile.net/vue/2.7.0/vue.js"></script>
|
||||
<!-- import JavaScript -->
|
||||
<script src="${base!}/assets/platform/plugins/element-ui/lib/index.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/jquery/jquery.js"></script>
|
||||
<script nonce="${cspNonce!}">
|
||||
var MSGINIT = "发送验证码"
|
||||
var MSGSCUCCESS = "{time}秒后重发"
|
||||
var MSGTIME = 60
|
||||
|
||||
function isPhone(str) {
|
||||
var reg = /^1[3456789]\d{9}$/
|
||||
return reg.test(str)
|
||||
}
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function() {
|
||||
var validateCode = function(rule, value, callback) {
|
||||
if (value.length !== 4) {
|
||||
callback(new Error("请输入4位数的验证码"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
activeName: "signin",
|
||||
msgText: MSGINIT,
|
||||
msgTime: MSGTIME,
|
||||
msgKey: false,
|
||||
year: 2018,
|
||||
captchaVisible: false,
|
||||
codeUrl: "",
|
||||
loginForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
platformKey: "",
|
||||
grantType: "captcha"
|
||||
},
|
||||
loginRules: {
|
||||
username: [{ required: true, trigger: ["blur", "change"], message: "用户名不能为空" }],
|
||||
password: [{ required: true, trigger: ["blur", "change"], message: "密码不能为空" }],
|
||||
platformCaptcha: [{ required: true, trigger: ["blur", "change"], message: "验证码不能为空" }]
|
||||
},
|
||||
regForm: {
|
||||
mobile: "",
|
||||
password: "",
|
||||
code: ""
|
||||
},
|
||||
regRules: {
|
||||
mobile: [
|
||||
{ required: true, message: "请输入手机号码", trigger: ["blur", "change"] },
|
||||
{ pattern: /^1[3456789]\d{9}$/, message: "请输入正确的手机号码" }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码", trigger: ["blur", "change"] },
|
||||
{ min: 8, max: 20, message: "密码长度必须是8-20位", trigger: "change" }
|
||||
],
|
||||
code: [{ required: true, trigger: ["blur", "change"], validator: validateCode }]
|
||||
},
|
||||
passwordType: "password",
|
||||
loading: false,
|
||||
regLoading: false,
|
||||
showDialog: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCode: function() {
|
||||
var _self = this
|
||||
$.get(
|
||||
"${base!}/platform/login/captcha",
|
||||
function(d) {
|
||||
_self.codeUrl = d.data.codeUrl
|
||||
_self.loginForm.platformKey = d.data.key
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
showPwd: function() {
|
||||
if (this.passwordType === "password") {
|
||||
this.passwordType = ""
|
||||
} else {
|
||||
this.passwordType = "password"
|
||||
}
|
||||
},
|
||||
handleSend: function() {
|
||||
var _self = this
|
||||
if (!isPhone(_self.regForm.mobile)) {
|
||||
_self.$message({
|
||||
message: "请输入手机号码",
|
||||
type: "error"
|
||||
})
|
||||
return false
|
||||
}
|
||||
if (_self.msgKey) return
|
||||
$.get(
|
||||
"${base!}/platform/login/sendsms?mobile=" + _self.regForm.mobile,
|
||||
function(d) {
|
||||
if (d.code === 0) {
|
||||
_self.$message({
|
||||
message: "短信发送成功,请注意查收!(实际没发送)",
|
||||
type: "success",
|
||||
duration: 8000
|
||||
})
|
||||
_self.msgText = MSGSCUCCESS.replace("{time}", _self.msgTime)
|
||||
_self.msgKey = true
|
||||
var time = setInterval(() => {
|
||||
_self.msgTime--
|
||||
_self.msgText = MSGSCUCCESS.replace("{time}", _self.msgTime)
|
||||
if (_self.msgTime === 0) {
|
||||
_self.msgTime = MSGTIME
|
||||
_self.msgText = MSGINIT
|
||||
_self.msgKey = false
|
||||
clearInterval(time)
|
||||
}
|
||||
}, 1000)
|
||||
} else {
|
||||
_self.$message({
|
||||
message: d.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
handleLogin: function() {
|
||||
var _self = this
|
||||
_self.$refs["loginForm"].validate(function(valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
"${base!}/platform/login/doLogin",
|
||||
_self.loginForm,
|
||||
function(d) {
|
||||
if (d.code == 0) {
|
||||
window.location.href = "${base!}/platform/home"
|
||||
} else {
|
||||
_self.$message({
|
||||
message: d.msg,
|
||||
type: "error"
|
||||
})
|
||||
_self.getCode()
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleReg: function() {
|
||||
var _self = this
|
||||
_self.$refs["regForm"].validate(function(valid) {
|
||||
if (valid) {
|
||||
_self.regLoading = true
|
||||
$.post(
|
||||
"${base!}/platform/login/doReg",
|
||||
_self.regForm,
|
||||
function(d) {
|
||||
if (d.code == 0) {
|
||||
_self.$message({
|
||||
message: "注册成功,请登录!",
|
||||
type: "success",
|
||||
duration: 5000
|
||||
})
|
||||
_self.activeName = "signin"
|
||||
_self.$set(_self.loginForm, "username", _self.regForm.mobile)
|
||||
_self.$refs["regForm"].resetFields()
|
||||
} else {
|
||||
_self.$message({
|
||||
message: d.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
_self.regLoading = false
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSocial: function(type) {
|
||||
this.$message.error("敬请期待")
|
||||
},
|
||||
errorMsg: function() {
|
||||
var errorMsg = "${error_message!}"
|
||||
if (errorMsg) {
|
||||
this.$message({
|
||||
message: errorMsg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function() {
|
||||
this.year = new Date().getFullYear()
|
||||
this.getCode()
|
||||
this.errorMsg()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html body {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #2c3e50;
|
||||
background: #f6f8f9;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.login-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100vh;
|
||||
background-image: url(${base!}/assets/platform/img/svg/background.svg);
|
||||
background-color: #f0f2f5;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.login-left {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
a {
|
||||
color: #1890ff;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-main {
|
||||
width: 323px;
|
||||
height: 455px;
|
||||
padding: 25px 25px 5px 25px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 10px auto 40px auto;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-form .el-input {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.login-form input {
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.login-form .input-icon {
|
||||
width: 14px;
|
||||
height: 39px;
|
||||
margin-left: 2px;
|
||||
}
|
||||
|
||||
.login-tip {
|
||||
font-size: 13px;
|
||||
color: #bfbfbf;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-code {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
width: 38%;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.login-code img {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-footer {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
width: 100%;
|
||||
height: 1rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
line-height: 1rem;
|
||||
color: #2c3e50;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.third-login {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin: 10px 0 30px 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.third-login .third-icon {
|
||||
margin: 0 20px;
|
||||
cursor: pointer;
|
||||
fill: rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.third-way {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 323px;
|
||||
}
|
||||
|
||||
.third-icon:hover {
|
||||
fill: rebeccapurple;
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
@@ -0,0 +1,186 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>上传文件</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
<script src="${base!}/assets/platform/plugins/vue/vue.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/axios/axios.js"></script>
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/vant/index.css" />
|
||||
<script src="${base!}/assets/platform/plugins/vant/index.js"></script>
|
||||
|
||||
<style>
|
||||
:root {
|
||||
--color-primary: #1867b0;
|
||||
--color-background: #f3f4f6;
|
||||
--color-text: #333;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: var(--color-background);
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
#app {
|
||||
padding: 20px;
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.upload-button {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.van-uploader__tip {
|
||||
color: var(--color-primary);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.van-button {
|
||||
background-color: var(--color-primary);
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app" v-cloak>
|
||||
<van-notice-bar
|
||||
left-icon="volume-o"
|
||||
text="请选择完文件后,一次性上传。"
|
||||
></van-notice-bar>
|
||||
<h1>文件上传</h1>
|
||||
<van-uploader
|
||||
v-model="fileList"
|
||||
:before-read="beforeRead"
|
||||
:after-read="afterRead"
|
||||
multiple
|
||||
:accept="accept"
|
||||
async
|
||||
:max-count="upload_number"
|
||||
title="选择文件"
|
||||
description=""
|
||||
tip=""
|
||||
></van-uploader>
|
||||
|
||||
<div class="upload-button">
|
||||
<van-button block type="primary" @click="onSubmit">同步到电脑</van-button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
fileList: [
|
||||
// { url: "http://localhost:8080/platform/sys/file/download?id=rn1v1efh2ag0aps59ult3ebudf", isImage: true }
|
||||
],
|
||||
// accept: "image/*",
|
||||
accept: "*",
|
||||
upload_number: 9,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
GetQueryString(name) {
|
||||
return new URLSearchParams(window.location.search).get(name)
|
||||
},
|
||||
beforeRead(file) {
|
||||
return true
|
||||
},
|
||||
afterRead(file) {},
|
||||
|
||||
buildUploadPromises(file) {
|
||||
return
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
const loading = this.$toast({
|
||||
duration: 0,
|
||||
overlay: true,
|
||||
forbidClick: true,
|
||||
message: "上传中",
|
||||
loadingType: "circular"
|
||||
})
|
||||
|
||||
Promise.all(
|
||||
this.fileList.map((f) => {
|
||||
const formData = new FormData()
|
||||
formData.append("file", f.file)
|
||||
return axios.post("/platform/sys/h5ScanCodeUploadFile/upload", formData).then((axiosResp) => {
|
||||
const resp = axiosResp.data
|
||||
if (resp.code === 0) {
|
||||
console.log(resp)
|
||||
f.name = f.file.name
|
||||
f.size = f.file.size
|
||||
f.status = null
|
||||
f.url = resp.data
|
||||
f.response = resp
|
||||
f.percentage = 100
|
||||
f.isImage = true
|
||||
delete f.file
|
||||
delete f.content
|
||||
console.log(f)
|
||||
} else {
|
||||
f.status = "fail"
|
||||
}
|
||||
})
|
||||
})
|
||||
)
|
||||
.then(() => {
|
||||
loading.close()
|
||||
console.log(this.fileList)
|
||||
this.syncPc()
|
||||
})
|
||||
.catch((error) => {
|
||||
loading.close()
|
||||
this.$toast.fail(error)
|
||||
})
|
||||
},
|
||||
|
||||
syncPc() {
|
||||
if(this.fileList.length===0){
|
||||
this.$toast('请先选择文件')
|
||||
return
|
||||
}
|
||||
console.log(this.fileList)
|
||||
|
||||
axios
|
||||
.post(
|
||||
"/platform/sys/h5ScanCodeUploadFile/syncPc",
|
||||
{
|
||||
files: JSON.stringify(this.fileList),
|
||||
timestamp: this.GetQueryString("timestamp")
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded;charset=UTF-8"
|
||||
}
|
||||
}
|
||||
)
|
||||
.then((resp) => {
|
||||
if (resp.data.code === 0) {
|
||||
this.$toast.success("上传成功,请关注电脑端信息")
|
||||
} else {
|
||||
this.$toast.fail(resp.data.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,310 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<style>
|
||||
.home .card-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
.quickEntries {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
||||
gap: 10px;
|
||||
max-height: 210px;
|
||||
}
|
||||
|
||||
.quickEntries .quickEntryItem img {
|
||||
max-width: 50px;
|
||||
}
|
||||
|
||||
.quickEntries .quickEntryItem {
|
||||
max-height: 210px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
transition:
|
||||
transform 0.3s ease-in-out,
|
||||
box-shadow 0.3s ease-in-out;
|
||||
}
|
||||
|
||||
.quickEntries .quickEntryItem span {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
.quickEntries .quickEntryItem:hover {
|
||||
cursor: pointer;
|
||||
background: #f3f3f3;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.quickEntries .quickEntryItem:hover {
|
||||
animation: shake 0.3s;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
25% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
50% {
|
||||
transform: translateX(5px);
|
||||
}
|
||||
75% {
|
||||
transform: translateX(-5px);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
.top-container {
|
||||
background: #fff;
|
||||
padding: 20px 10px;
|
||||
display: grid;
|
||||
grid-template-columns: 72px 3fr 1fr 1fr;
|
||||
grid-column-gap: 20px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.top-container .avatar-wrapper {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
line-height: 72px;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.top-container .avatar-wrapper img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.top-container .basic-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.top-container .basic-wrapper .welcome {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5em;
|
||||
color: rgba(0, 0, 0, 0.8509803921568627);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 1.75rem;
|
||||
}
|
||||
|
||||
.top-container .basic-wrapper .text-secondary {
|
||||
color: rgba(0, 0, 0, 0.45098039215686275);
|
||||
}
|
||||
|
||||
.top-container .date-wrap {
|
||||
padding-right: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.top-container .date-wrap .date {
|
||||
color: rgba(0, 0, 0, 0.8509803921568627);
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 1.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.activity_list {
|
||||
width: calc(100% - 300px);
|
||||
max-width: calc(100% - 300px);
|
||||
padding: 15px 0;
|
||||
}
|
||||
|
||||
.wrap {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 767px) {
|
||||
.wrap {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.wrap .activity_list {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-calendar {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.el-calendar .el-calendar__header {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.el-calendar__body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.el-timeline {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-calendar-table td {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.el-calendar-table td.is-selected {
|
||||
border-radius: 9999px;
|
||||
background-color: unset !important;
|
||||
}
|
||||
|
||||
.el-calendar-table .el-calendar-day {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
padding: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.el-calendar-table .el-calendar-day:hover {
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.tips {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.el-timeline-item__content {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.el-timeline-item__content:hover {
|
||||
color: #1890ff;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<div class="padding10X">
|
||||
<div class="top-container">
|
||||
<div class="avatar-wrapper">
|
||||
<img
|
||||
v-if="!$store.state.user.sex || $store.state.user.sex==='男'"
|
||||
alt=""
|
||||
src="/assets/platform/img/home/avatar_boy.png"
|
||||
class="user-avatar"
|
||||
/>
|
||||
<img v-else alt="" src="/assets/platform/img/home/avatar_girl.png" class="user-avatar" />
|
||||
</div>
|
||||
<div class="basic-wrapper">
|
||||
<div class="welcome">{{$store.state.user.username}},开始您一天的工作吧!</div>
|
||||
<span class="text-secondary">{{$store.state.user.unit.name}} {{$store.state.user.union.name}}</span>
|
||||
<span class="text-secondary">{{$store.state.user.sex}} {{$store.state.user.userState}} {{$store.state.user.personType}}</span>
|
||||
</div>
|
||||
<div class="weather-wrap"></div>
|
||||
<div class="date-wrap">
|
||||
<div class="date">{{$moment().format('YYYY年MM月DD日')}}</div>
|
||||
<div class="week">{{$moment().format('dddd')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<div class="card-title" slot="header">快速入口</div>
|
||||
<div class="quickEntries">
|
||||
<div v-for="item in quickEntries" :key="item.id" @click="pjaxReplace(item.href)" class="quickEntryItem">
|
||||
<svg-icon :name="item.icon" :size="40" style="color: red"></svg-icon>
|
||||
<span>{{item.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-row :gutter="20" class="mt10">
|
||||
<el-col :span="16"></el-col>
|
||||
<el-col :span="8">
|
||||
<el-card shadow="never">
|
||||
<div class="card-title" slot="header">最新活动</div>
|
||||
<div v-for="item in activityOptions" :key="item.id">
|
||||
<div style="width: 100%">
|
||||
<div style="padding: 14px; background-color: #fff; position: relative">
|
||||
<div style="display: flex; justify-content: center; align-items: center">
|
||||
<!-- :src="item.cover"-->
|
||||
<el-image src="https://img.yzcdn.cn/vant/t-thirt.jpg" alt="" style="margin: 3px 14px 0 0; width: 84px; height: 84px">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
<div style="flex: 1">
|
||||
<div style="line-height: 22px; ont-size: 16px; color: #111">{{item.name}}</div>
|
||||
<div style="margin-top: 6px; line-height: 20px; color: #999">{{item.startDate}}-{{item.endDate}}</div>
|
||||
</div>
|
||||
<el-tooltip content="去报名" placement="top-start">
|
||||
<div @click="enterActivity(item)">
|
||||
<svg-icon name="ant-design_tag-twotone" :size="30" style="color: var(--color-primary)"></svg-icon>
|
||||
</div>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-button type="primary" icon="el-icon-edit" @click="$refs.icon.showIconModal()">cxccc</el-button>
|
||||
<icon-selector ref="icon" v-model="icon"></icon-selector>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
components: {
|
||||
"icon-selector": httpVueLoader("/components/plugins/sysIconSelector/index.vue?v=" + new Date().getTime())
|
||||
},
|
||||
store,
|
||||
data: function () {
|
||||
return {
|
||||
quickEntries: [],
|
||||
activityOptions: [],
|
||||
icon: "alipay"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
listRecommendApp() {
|
||||
this.$axios.post("/platform/home/listRecommendApp").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.quickEntries = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
listActivity() {
|
||||
this.$axios.post("/platform/h5/listHomeActivity").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.activityOptions = resp.data
|
||||
}
|
||||
})
|
||||
},
|
||||
enterActivity(item) {
|
||||
this.$store.dispatch("pjaxRoute", item.url)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.listRecommendApp()
|
||||
this.listActivity()
|
||||
this.listProcessInstance()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped></style>
|
||||
|
||||
<!--# } #-->
|
||||
@@ -0,0 +1,167 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称" prop="name">
|
||||
<el-input v-model="pageForm.name" clearable placeholder="请输入名称"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<el-table :data="tableData" header-align="center" border style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="50px" :method="indexMethod"></el-table-column>
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="pc端链接" prop="url"></el-table-column>
|
||||
<el-table-column label="h5端链接" prop="h5Url"></el-table-column>
|
||||
<el-table-column label="封面" prop="cover" width="80px">
|
||||
<template slot-scope="scope">
|
||||
<el-image :src="scope.row.cover" v-if="scope.row.icon" style="width: 30px; height: 30px"></el-image>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="是否置顶" prop="top" width="100px">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" v-if="scope.row.top" type="success">是</el-tag>
|
||||
<el-tag size="mini" v-else type="info">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="推送大图" prop="push" width="100px">
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" v-if="scope.row.push" type="success">是</el-tag>
|
||||
<el-tag size="mini" v-else type="info">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="关联类路径" prop="classPath" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="状态" prop="enable" width="80px">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="!scope.row.enable" class="fa fa-circle text-danger ml5"></i>
|
||||
<i v-else class="fa fa-circle text-success ml5"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="250px">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="danger" size="mini" @click="disable(scope.row.id)" v-if="scope.row.enable">关闭
|
||||
</el-link>
|
||||
<el-link type="primary" size="mini" @click="enable(scope.row.id)" v-if="!scope.row.enable">开启
|
||||
</el-link>
|
||||
<el-link type="primary" size="mini" @click="topUp(scope.row.id)" v-if="!scope.row.top">置顶
|
||||
</el-link>
|
||||
<el-link type="danger" size="mini" @click="cancelTopUp(scope.row.id)" v-if="scope.row.top">
|
||||
取消置顶
|
||||
</el-link>
|
||||
<el-link type="primary" size="mini" @click="push(scope.row.id)" v-if="!scope.row.push">推送大图
|
||||
</el-link>
|
||||
<el-link type="danger" size="mini" @click="cancelPush(scope.row.id)" v-if="scope.row.push">
|
||||
取消推送大图
|
||||
</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
disable(id) {
|
||||
this.$confirm("您确定要关闭吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/disable", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
enable(id) {
|
||||
this.$confirm("您确定要开启吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/enable", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
topUp(id) {
|
||||
this.$confirm("您确定要置顶吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/topUp", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
cancelTopUp(id) {
|
||||
this.$confirm("您确定要取消置顶吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/cancelTopUp", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
push(id) {
|
||||
this.$confirm("您确定要推送大图吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/push", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
cancelPush(id) {
|
||||
this.$confirm("您确定要取消推送大图吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/cancelPush", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,223 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<el-row type="flex" style="column-gap: 10px">
|
||||
<el-select placeholder="请选择日志类型" size="medium" v-model="pageForm.searchType">
|
||||
<el-option :key="item.value" :label="item.label" :value="item.value"
|
||||
v-for="item in options"></el-option>
|
||||
</el-select>
|
||||
<el-date-picker
|
||||
:default-time="['00:00:00', '23:59:59']"
|
||||
:picker-options="pickerOptions"
|
||||
align="right"
|
||||
end-placeholder="结束日期"
|
||||
range-separator="至"
|
||||
size="medium"
|
||||
start-placeholder="开始日期"
|
||||
type="datetimerange"
|
||||
v-model="searchDate"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
></el-date-picker>
|
||||
<el-button @click="doSearch" icon="el-icon-search" size="medium" type="primary"></el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:default-sort="{prop: 'createdat', order: 'descending'}"
|
||||
@sort-change="pageOrder"
|
||||
header-align="center"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-table-column align="center" header-align="center" label="操作人" prop="username" sortable>
|
||||
<template slot-scope="scope">{{scope.row.username+'/'+scope.row.loginname}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作时间" prop="createdat" sortable
|
||||
width="200">
|
||||
<template slot-scope="scope">{{formatAt(scope.row.createdat)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="日志类型" prop="type" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.type=='platform'">平台日志</span>
|
||||
<span v-if="scope.row.type=='info'">登录日志</span>
|
||||
<span v-if="scope.row.type=='api'">接口日志</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" header-align="center" label="执行动作" prop="tag" width="200"
|
||||
show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="日志内容" prop="msg" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" label="执行类" prop="src"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" label="IP" prop="ip"></el-table-column>
|
||||
<el-table-column label="操作" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-button @click="openView(scope.row)" size="mini" type="primary">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog title="查看" :visible.sync="dialogVisible" width="70%">
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="操作人">{{row.username}}</el-descriptions-item>
|
||||
<el-descriptions-item label="操作时间">{{formatAt(row.createdat)}}</el-descriptions-item>
|
||||
<el-descriptions-item label="日志类型">
|
||||
<span v-if="row.type=='platform'">平台日志</span>
|
||||
<span v-if="row.type=='info'">登录日志</span>
|
||||
<span v-if="row.type=='api'">接口日志</span>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="执行动作" :span="2">{{row.tag}}</el-descriptions-item>
|
||||
<el-descriptions-item label="日志内容" :span="2">{{row.msg}}</el-descriptions-item>
|
||||
<el-descriptions-item label="执行类" :span="2">{{row.src}}</el-descriptions-item>
|
||||
<el-descriptions-item label="IP" :span="2">{{row.ip}}</el-descriptions-item>
|
||||
<el-descriptions-item label="参数" :span="2">{{row.param}}</el-descriptions-item>
|
||||
<el-descriptions-item label="结果" :span="2">{{row.result}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
options: [
|
||||
{
|
||||
value: "",
|
||||
label: "全部类型"
|
||||
},
|
||||
{
|
||||
value: "platform",
|
||||
label: "平台日志"
|
||||
},
|
||||
{
|
||||
value: "info",
|
||||
label: "登录日志"
|
||||
},
|
||||
{
|
||||
value: "api",
|
||||
label: "接口日志"
|
||||
}
|
||||
],
|
||||
pickerOptions: {
|
||||
disabledDate: function (time) {
|
||||
var threeMonths = moment().add(-3, "months").valueOf()
|
||||
return time.getTime() > Date.now() || time.getTime() < threeMonths
|
||||
},
|
||||
shortcuts: [
|
||||
{
|
||||
text: "最近一周",
|
||||
onClick: function (picker) {
|
||||
var end = new Date()
|
||||
var start = new Date()
|
||||
var temp = moment().add(-1, "weeks").valueOf()
|
||||
start.setTime(temp)
|
||||
picker.$emit("pick", [start, end])
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "最近一个月",
|
||||
onClick: function (picker) {
|
||||
var end = new Date()
|
||||
var start = new Date()
|
||||
var temp = moment().add(-1, "months").valueOf()
|
||||
start.setTime(temp)
|
||||
picker.$emit("pick", [start, end])
|
||||
}
|
||||
},
|
||||
{
|
||||
text: "最近三个月",
|
||||
onClick: function (picker) {
|
||||
var end = new Date()
|
||||
var start = new Date()
|
||||
var temp = moment().add(-3, "months").valueOf()
|
||||
start.setTime(temp)
|
||||
picker.$emit("pick", [start, end])
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "createdat",
|
||||
pageOrderBy: "descending",
|
||||
searchDate: "",
|
||||
searchType: ""
|
||||
},
|
||||
searchDate: [],
|
||||
dialogVisible: false,
|
||||
row: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatAt: function (val) {
|
||||
if (val > 0) return moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
},
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
doSearch: function () {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
if (self.searchDate) {
|
||||
self.pageForm.searchDate = self.searchDate.toString()
|
||||
} else {
|
||||
self.pageForm.searchDate = ""
|
||||
}
|
||||
$.post(
|
||||
base + "/platform/sys/log/data",
|
||||
self.pageForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list
|
||||
self.pageForm.totalCount = data.data.totalCount
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
openView(row) {
|
||||
this.row = {...row}
|
||||
this.dialogVisible = true
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,350 @@
|
||||
<!doctype html>
|
||||
<html lang="${lang,escape}">
|
||||
<head>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1" />
|
||||
<meta name="description" content="${AppName!}" />
|
||||
<title>${AppName!}</title>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- import CSS -->
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/plugins/element-ui/lib/theme-chalk/index.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/fonts/themify-icons.css" />
|
||||
<link rel="stylesheet" href="${base!}/assets/platform/css/common.css" />
|
||||
</head>
|
||||
<body>
|
||||
<!--[if lt IE 9]>
|
||||
<div class="ie-must-go-die">
|
||||
<div class="ie-container">
|
||||
<h1>您正在IE的世界中</h1>
|
||||
<p>本系统不支持IE9以下版本浏览器访问。</p>
|
||||
<a href="https://browsehappy.com" target="_blank">您可以点击此处,下载Chrome/Firefox后再次访问本页面。</a>
|
||||
</div>
|
||||
</div>
|
||||
<![endif]-->
|
||||
<div id="app" v-cloak>
|
||||
<img src="/assets/platform/img/login_bg.png" class="login-bg-img" alt="" />
|
||||
<div class="login-container">
|
||||
<div class="img">
|
||||
<img src="/assets/platform/img/login_bg_center.svg" />
|
||||
</div>
|
||||
<div class="login-box">
|
||||
<div class="login-form">
|
||||
<div class="my-3 text-6xl font-semibold">${AppName!}${base!}</div>
|
||||
<div class="flx-center-center">
|
||||
<div class="p-12 bg-white rounded-3xl" style="box-sizing: border-box">
|
||||
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" auto-complete="on" label-position="left">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="loginForm.username"
|
||||
type="text"
|
||||
auto-complete="off"
|
||||
placeholder="账号"
|
||||
prefix-icon="el-icon-user"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
v-model="loginForm.password"
|
||||
type="password"
|
||||
auto-complete="off"
|
||||
placeholder="密码"
|
||||
@keyup.enter.native="handleLogin"
|
||||
prefix-icon="el-icon-lock"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="!captchaDisabled" prop="platformCaptcha">
|
||||
<el-input
|
||||
v-model="loginForm.platformCaptcha"
|
||||
auto-complete="off"
|
||||
placeholder="验证码"
|
||||
style="width: 60%"
|
||||
@keyup.enter.native="handleLogin"
|
||||
prefix-icon="el-icon-key"
|
||||
></el-input>
|
||||
<div class="login-code">
|
||||
<img :src="codeUrl" @click="getCode" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 100%">
|
||||
<el-button
|
||||
:loading="loading"
|
||||
size="medium"
|
||||
type="primary"
|
||||
style="width: 100%; --color-primary: #1867b0"
|
||||
@click.native.prevent="handleLogin"
|
||||
>
|
||||
<span v-if="!loading">登 录</span>
|
||||
<span v-else>登 录 中...</span>
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
<!-- import Vue before Element -->
|
||||
<script src="${base!}/assets/platform/plugins/vue/vue.js"></script>
|
||||
<!-- import JavaScript -->
|
||||
<script src="${base!}/assets/platform/plugins/element-ui/lib/index.js"></script>
|
||||
<!-- import axios -->
|
||||
<script src="${base!}/assets/platform/plugins/axios/axios.js"></script>
|
||||
<!-- import Jquery -->
|
||||
<script src="${base!}/assets/platform/plugins/jquery/jquery.js"></script>
|
||||
<!-- import SM2 -->
|
||||
<script src="${base!}/assets/platform/plugins/sm-crypto/sm2.js"></script>
|
||||
<!-- import commonUtil -->
|
||||
<script src="${base!}/assets/platform/js/util/commonUtil.js"></script>
|
||||
<!-- import jsencrypt-->
|
||||
<script src="${base!}/assets/platform/plugins/jsencrypt/jsencrypt.min.js"></script>
|
||||
<script nonce="${cspNonce!}">
|
||||
// 在加载 lodash 后、使用前插入
|
||||
const originalDefaultsDeep = window._.defaultsDeep;
|
||||
window._.defaultsDeep = function(...args) {
|
||||
// 先对所有参数做原型污染清洗
|
||||
const cleanArgs = args.map(arg => sanitizeForPrototypePollution(arg));
|
||||
return originalDefaultsDeep.apply(window._, cleanArgs);
|
||||
};
|
||||
|
||||
function sanitizeForPrototypePollution(obj) {
|
||||
if (obj === null || typeof obj !== 'object') return obj;
|
||||
if (Array.isArray(obj)) return obj.map(sanitizeForPrototypePollution);
|
||||
|
||||
const clean = {};
|
||||
for (const key in obj) {
|
||||
if (!Object.hasOwn(obj, key)) continue;
|
||||
if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
|
||||
continue;
|
||||
}
|
||||
clean[key] = sanitizeForPrototypePollution(obj[key]);
|
||||
}
|
||||
return clean;
|
||||
}
|
||||
</script>
|
||||
<script nonce="${cspNonce!}">
|
||||
const DISABLE_LOGIN_CAPTCHA = ["localhost", "127.0.0.1", "::1"].includes(window.location.hostname)
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
codeUrl: "",
|
||||
captchaDisabled: DISABLE_LOGIN_CAPTCHA,
|
||||
loginForm: {
|
||||
username: "",
|
||||
password: "",
|
||||
platformKey: "",
|
||||
grantType: "captcha"
|
||||
},
|
||||
loginRules: {
|
||||
username: [{ required: true, trigger: ["blur", "change"], message: "账号不能为空" }],
|
||||
password: [{ required: true, trigger: ["blur", "change"], message: "密码不能为空" }],
|
||||
platformCaptcha: []
|
||||
},
|
||||
loading: false,
|
||||
rasParams: {
|
||||
publicKey: '',
|
||||
keyId: '',
|
||||
expireAt: 0,
|
||||
},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCode() {
|
||||
if (this.captchaDisabled) {
|
||||
return
|
||||
}
|
||||
axios.post("/platform/login/captcha").then((resp) => {
|
||||
if (resp.data.code === 0) {
|
||||
this.codeUrl = resp.data.data.codeUrl
|
||||
this.loginForm.platformKey = resp.data.data.key
|
||||
}
|
||||
})
|
||||
},
|
||||
handleLogin() {
|
||||
this.$refs.loginForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
await this.ensurePublicKey()
|
||||
const { publicKey, keyId } = this.rasParams
|
||||
if (!publicKey || !keyId) {
|
||||
this.$message({
|
||||
message: "Login key unavailable, please refresh and try again",
|
||||
type: "error"
|
||||
})
|
||||
return
|
||||
}
|
||||
// 加密密码
|
||||
const encrypt = new JSEncrypt()
|
||||
encrypt.setPublicKey(publicKey)
|
||||
const encryptedPassword = encrypt.encrypt(this.loginForm.password)
|
||||
axios
|
||||
.post("/platform/login/doLogin", null, {
|
||||
params: {
|
||||
...this.loginForm,
|
||||
//password: window.btoa(this.loginForm.password)
|
||||
password: encryptedPassword,
|
||||
keyId: keyId
|
||||
}
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.data.code === 0) {
|
||||
this.$message.success("登录成功")
|
||||
const redirect = new URLSearchParams(window.location.search).get("redirect")
|
||||
if (redirect) {
|
||||
window.location.href = "${base!}" + redirect || ""
|
||||
} else {
|
||||
window.location.href = "${base!}/platform/home"
|
||||
}
|
||||
} else {
|
||||
this.getCode()
|
||||
this.$message({
|
||||
message: resp.data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
} else {
|
||||
console.log("cccccc")
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取公钥
|
||||
fetchPublicKey() {
|
||||
return axios.get("/platform/login/publicKey").then(res => {
|
||||
this.rasParams = res.data.data
|
||||
})
|
||||
},
|
||||
ensurePublicKey() {
|
||||
const refreshBeforeMillis = 30 * 1000
|
||||
if (this.rasParams.publicKey && this.rasParams.keyId && Date.now() < this.rasParams.expireAt - refreshBeforeMillis) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return this.fetchPublicKey()
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.fetchPublicKey()
|
||||
this.getCode()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style scoped>
|
||||
#app {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
html body {
|
||||
position: relative;
|
||||
height: 100vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #2c3e50;
|
||||
background: #f6f8f9;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.login-container {
|
||||
grid-template-columns: 1fr !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.login-container .img {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.login-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.login-container .img {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.login-container .img img {
|
||||
z-index: 0;
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.login-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.login-form {
|
||||
width: 500px;
|
||||
}
|
||||
|
||||
.font-semibold {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.text-6xl {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.my-3 {
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.p-12 {
|
||||
padding: 48px;
|
||||
}
|
||||
|
||||
.bg-white {
|
||||
--tw-bg-opacity: 1;
|
||||
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
|
||||
}
|
||||
|
||||
.rounded-3xl {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
|
||||
.login-code {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
width: 38%;
|
||||
height: 38px;
|
||||
}
|
||||
|
||||
.login-code img {
|
||||
vertical-align: middle;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.login-bg-img {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.login-container > div.img {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.login-container .login-box .login-form {
|
||||
width: 100vw !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</html>
|
||||
@@ -0,0 +1,261 @@
|
||||
const SYS_MENU_BASIC_FORM_COMPONENT = {
|
||||
template: /*language=HTML*/ `
|
||||
<el-dialog :title="formData.id ? '编辑菜单' : '新增菜单'" :show-close="false" :visible.sync="visibleDialog"
|
||||
:close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item label="所属模块" prop="moduleId">
|
||||
<el-select v-model="formData.moduleId" clearable placeholder="请选择模块">
|
||||
<el-option v-for="item in moduleOptions" :value="item.id" :key="item.id"
|
||||
:label="item.name"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="parentId" label="上级菜单" label-width="80px">
|
||||
<el-cascader
|
||||
:options="menuOptions"
|
||||
:props="props"
|
||||
v-model="parentMenu"
|
||||
placeholder="不选择则为顶级"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="菜单名称">
|
||||
<el-input maxlength="100" placeholder="菜单名称" v-model="formData.name"
|
||||
type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="aliasName" label="菜单别名">
|
||||
<el-input maxlength="100" placeholder="菜单名称" v-model="formData.aliasName"
|
||||
type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission" label="权限标识">
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="权限标识,如 sys.manager.menu"
|
||||
v-model="formData.permission"
|
||||
auto-complete="off"
|
||||
tabindex="5"
|
||||
type="text"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="href" label="URL">
|
||||
<el-input maxlength="100" placeholder="URL" v-model="formData.href" auto-complete="off" tabindex="6"
|
||||
type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="picIcon" label="图片图标">
|
||||
<!-- <template v-if="platform==='PC'">-->
|
||||
<!-- <el-input maxlength="100" placeholder="图标" v-model="formData.icon"></el-input>-->
|
||||
<!-- <i :class="formData.icon" v-if="formData.icon"></i>-->
|
||||
<!-- </template> -->
|
||||
<!-- <el-input maxlength="100" -->
|
||||
<!-- v-if="platform==='PC'"-->
|
||||
<!-- placeholder="图标" -->
|
||||
<!-- v-model="formData.icon" -->
|
||||
<!-- auto-complete="off" -->
|
||||
<!-- tabindex="7"-->
|
||||
<!-- type="text">-->
|
||||
<!-- <template slot="append">-->
|
||||
<!-- <el-button @click="$refs.iconSelector.showIconModal(formData.icon)">图标选择</el-button>-->
|
||||
<!-- <icon-selector ref="iconSelector" v-model="formData.icon"></icon-selector>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-input>-->
|
||||
<file-upload
|
||||
:value.sync="formData.picIcon"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="图标" prop="icon">
|
||||
<el-input maxlength="100" placeholder="图标" v-model="formData.icon"></el-input>
|
||||
<i :class="formData.icon" v-if="formData.icon"></i>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="数据权限">
|
||||
<el-radio-group @change="formRadioChange" v-model="formData.children" size="mini">
|
||||
<el-radio label="false">无</el-radio>
|
||||
<el-radio label="true">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.children=='true'"
|
||||
v-for="(menu, index) in formData.buttons"
|
||||
:label="'权限' + (index+1)"
|
||||
:key="menu.key"
|
||||
:prop="'buttons[' + index + ']'"
|
||||
:rules="{required: true,validator: validateMenu,trigger: ['blur','change']}"
|
||||
>
|
||||
<el-row :gutter="2">
|
||||
<el-col :span="6">
|
||||
<el-input v-model="menu.name" placeholder="权限名称"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="13">
|
||||
<el-input v-model="menu.permission"
|
||||
placeholder="权限标识,如 sys.manager.menu.add"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button @click.prevent="formRemoveMenu(menu)" icon="el-icon-delete"
|
||||
size="small"></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-row v-if="formData.children=='true'" style="text-align: right; padding-right: 17px">
|
||||
<el-button size="small" @click="formAddMenu" icon="el-icon-circle-plus">添加</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="visibleDialog = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
visibleDialog: false,
|
||||
formData: {
|
||||
buttons: []
|
||||
},
|
||||
formRules: {
|
||||
moduleId: [{ required: true, message: "必填", trigger: "blur" }],
|
||||
name: [{ required: true, message: "必填", trigger: "blur" }],
|
||||
permission: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
},
|
||||
menuOptions: [],
|
||||
parentMenu: [],
|
||||
props: {
|
||||
checkStrictly: true,
|
||||
multiple: false,
|
||||
label: "name",
|
||||
value: "id"
|
||||
},
|
||||
platform: null,
|
||||
moduleOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(id, platform) {
|
||||
this.visibleDialog = true
|
||||
this.platform = platform
|
||||
this.listFullTree()
|
||||
this.listModule()
|
||||
if (id == null) {
|
||||
this.formData = {
|
||||
buttons: []
|
||||
}
|
||||
} else {
|
||||
this.detail(id)
|
||||
}
|
||||
},
|
||||
addChildMenu(data, platform) {
|
||||
this.visibleDialog = true
|
||||
this.platform = platform
|
||||
this.formData = {
|
||||
moduleId: data.moduleId,
|
||||
buttons: []
|
||||
}
|
||||
|
||||
this.$axios.post("/platform/sys/menu/editMenu/" + data.id).then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (res.data.parentIds && res.data.parentIds.length > 0) {
|
||||
this.parentMenu = res.data.parentIds
|
||||
} else {
|
||||
this.parentMenu = []
|
||||
}
|
||||
this.parentMenu.push(data.id)
|
||||
}
|
||||
})
|
||||
|
||||
this.listFullTree()
|
||||
this.listModule()
|
||||
},
|
||||
listFullTree() {
|
||||
this.$axios.post(loc() + "/fullTree", { platform: this.platform }).then((res) => {
|
||||
this.menuOptions = res.data
|
||||
})
|
||||
},
|
||||
|
||||
detail(id) {
|
||||
this.$axios.post("/platform/sys/menu/editMenu/" + id).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = res.data
|
||||
this.parentMenu = res.data.parentIds
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
submit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.parentMenu) {
|
||||
this.formData.parentId = this.parentMenu[this.parentMenu.length - 1] || ""
|
||||
} else {
|
||||
this.formData.parentId = ""
|
||||
}
|
||||
const url = this.formData.id ? "/platform/sys/menu/editMenuDo" : "/platform/sys/menu/addDo"
|
||||
this.$axios
|
||||
.post(url, {
|
||||
menu: JSON.stringify(this.formData),
|
||||
platform: this.platform
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("保存成功")
|
||||
this.visibleDialog = false
|
||||
this.$emit("refresh", null)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
formRadioChange: function (val) {
|
||||
this.formData.children = val
|
||||
if (val) {
|
||||
this.formData.buttons = [
|
||||
{
|
||||
name: "",
|
||||
permission: "",
|
||||
key: Date.now()
|
||||
}
|
||||
]
|
||||
} else {
|
||||
this.formData.buttons = []
|
||||
}
|
||||
},
|
||||
formAddMenu() {
|
||||
this.formData.buttons.push({
|
||||
name: "",
|
||||
permission: "",
|
||||
key: Date.now()
|
||||
})
|
||||
},
|
||||
formRemoveMenu(menu) {
|
||||
const index = this.formData.buttons.indexOf(menu)
|
||||
if (index !== -1) {
|
||||
this.formData.buttons.splice(index, 1)
|
||||
}
|
||||
},
|
||||
//验证动态添加行数据
|
||||
validateMenu: function (rule, value, callback) {
|
||||
if (value.name === "") {
|
||||
callback(new Error("权限名称不能为空"))
|
||||
} else if (value.permission === "") {
|
||||
callback(new Error("权限标识不能为空"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
|
||||
listModule() {
|
||||
this.$axios.post(loc() + "/listModule", { platform: this.platform }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.moduleOptions = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.context-menu {
|
||||
position: fixed;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
z-index: 3000;
|
||||
list-style: none;
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
.context-menu li {
|
||||
padding: 7px 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.context-menu li:hover {
|
||||
background: #f2f6fc;
|
||||
}
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="平台">
|
||||
<el-select v-model="pageForm.platform" :clearable="false" @change="listModule();doSearch();">
|
||||
<el-option v-for="item in dict.type.SYS_MENU_PLATFORM" :value="item.code" :key="item.value" :label="item.label"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="模块">
|
||||
<el-select v-model="pageForm.moduleId" clearable placeholder="请选择模块">
|
||||
<el-option v-for="item in moduleOptions" :value="item.id" :key="item.id" :label="item.name"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="菜单名称">
|
||||
<el-input v-model="pageForm.menuName" clearable placeholder="请填写名称查询" style="width: 100%"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool>
|
||||
<el-button @click="$refs.basicFormRef.onOpen(null,pageForm.platform)" size="small" type="primary" icon="el-icon-plus">新建菜单</el-button>
|
||||
<el-button @click="$refs.sortRef.onOpen(pageForm.platform)" size="small" type="primary" icon="el-icon-sort">排序</el-button>
|
||||
<el-button @click="$refs.recommendSettingRef.onOpen(pageForm.platform, 'app')" size="small" type="primary" icon="el-icon-monitor">
|
||||
首页推荐应用
|
||||
</el-button>
|
||||
<el-button @click="$refs.recommendSettingRef.onOpen(pageForm.platform, 'service')" size="small" type="primary" icon="el-icon-monitor">
|
||||
首页推荐服务
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:highlight-current-row="true"
|
||||
:key="tableKey"
|
||||
:load="loadChild"
|
||||
@row-contextmenu="openMenu"
|
||||
:expand-row-keys="expandedRowKeysRenew"
|
||||
@expand-change="handleExpandChange"
|
||||
ref="tableData"
|
||||
style="width: 100%"
|
||||
lazy
|
||||
row-key="id"
|
||||
>
|
||||
<el-table-column align="left" header-align="left" label="菜单名称" prop="name" width="200"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="排序编码" prop="location" width="120">
|
||||
<template slot-scope="{row}">
|
||||
<el-input-number
|
||||
v-model="row.location"
|
||||
:controls="false"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
size="mini"
|
||||
style="width: 88px"
|
||||
@change="updateLocation(row)"
|
||||
></el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="菜单图标" prop="icon" width="100">
|
||||
<template slot-scope="scope">
|
||||
<template v-if="pageForm.platform==='PC'">
|
||||
<svg-icon :name="scope.row.icon" v-if="scope.row.icon" :size="20"></svg-icon>
|
||||
</template>
|
||||
<template v-else-if="pageForm.platform==='H5'">
|
||||
<el-image :src="scope.row.icon" v-if="scope.row.icon" style="width: 30px; height: 30px"></el-image>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="URL" prop="href"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="资源类型" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<i style="font-weight: bold" v-if="scope.row.type=='menu'">菜单</i>
|
||||
<i v-if="scope.row.type=='data'">数据</i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="所属模块" prop="moduleId">
|
||||
<template slot-scope="scope">
|
||||
<i style="font-weight: bold">{{moduleOptions.find(item=>item.id==scope.row.moduleId)?.name}}</i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="权限标识" prop="permission"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="启用状态" prop="disabled">
|
||||
<template slot-scope="scope">
|
||||
<i class="fa fa-circle text-danger ml5" v-if="scope.row.disabled"></i>
|
||||
<i class="fa fa-circle text-success ml5" v-if="!scope.row.disabled"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="操作" prop="website" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<!-- <el-dropdown-item :command="{type:'add',id:scope.row.id,name:scope.row.name}" v-if="scope.row.type=='menu'">-->
|
||||
<!-- 添加子菜单-->
|
||||
<!-- </el-dropdown-item>-->
|
||||
<el-dropdown-item :command="{type:'enable',id:scope.row.id,name:scope.row.name,row:scope.row}" divided>
|
||||
启用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',id:scope.row.id,name:scope.row.name,row:scope.row}">禁用</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'editMenu',id:scope.row.id,name:scope.row.name}" divided v-if="scope.row.type=='menu'">
|
||||
修改
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'editData',id:scope.row.id,name:scope.row.name}" divided v-if="scope.row.type=='data'">
|
||||
修改
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',id:scope.row.id,name:scope.row.name}" v-if="scope.row.path!='0001'">
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 右键菜单 -->
|
||||
<ul v-show="menuVisible" class="context-menu" :style="{left: menuLeft + 'px', top: menuTop + 'px'}">
|
||||
<li @click="handleAddMenu(currentRow)">新建菜单</li>
|
||||
<li v-if="showAddMenu" @click="handleAddChildMenu(currentRow)">添加子菜单</li>
|
||||
<li @click="handleEdit(currentRow)">编辑</li>
|
||||
<li @click="handleDelete(currentRow)">删除</li>
|
||||
</ul>
|
||||
|
||||
</el-card>
|
||||
|
||||
|
||||
<basic-form ref="basicFormRef" @refresh="loadChildByExpandedKeys"></basic-form>
|
||||
<permission-form ref="permissionFormRef" @load-child="loadChildByExpandedKeys"></permission-form>
|
||||
<sort ref="sortRef" @refresh="doSearch"></sort>
|
||||
<recommend-setting ref="recommendSettingRef" @refresh="doSearch"></recommend-setting>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("permissionForm.js"){}#-->
|
||||
<!--#include("basicForm.js"){}#-->
|
||||
<!--#include("sort.js"){}#-->
|
||||
<!--#include("recommendSetting.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
dicts: ["SYS_MENU_PLATFORM"],
|
||||
components: {
|
||||
"basic-form": SYS_MENU_BASIC_FORM_COMPONENT,
|
||||
"permission-form": SYS_MENU_PERMISSION_FORM_COMPONENT,
|
||||
sort: SYS_MENU_SORT_COMPONENT,
|
||||
"recommend-setting": SYS_MENU_RECOMMEND_SETTING_COMPONENT
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
pageForm: {},
|
||||
tableData: [],
|
||||
tableKey: "",
|
||||
moduleOptions: [],
|
||||
// 保存展开状态的数组
|
||||
expandedRowKeysRenew: [],
|
||||
tableTreeRefreshTool: [],
|
||||
|
||||
// 右键打开菜单相关
|
||||
currentRow: null,
|
||||
menuVisible: false,
|
||||
menuLeft: 0,
|
||||
menuTop: 0,
|
||||
showAddMenu: false,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/* 右键行 */
|
||||
openMenu(row, column, event) {
|
||||
this.showAddMenu = row.type === 'menu';
|
||||
// 阻止浏览器默认菜单
|
||||
event.preventDefault();
|
||||
this.currentRow = row;
|
||||
|
||||
this.menuVisible = true;
|
||||
this.$nextTick(() => {
|
||||
const menu = this.$el.querySelector('.context-menu');
|
||||
const h = menu.offsetHeight || 100;
|
||||
const sh = document.documentElement.clientHeight;
|
||||
let top = event.clientY;
|
||||
if (top + h > sh) top -= h;
|
||||
this.menuLeft = event.clientX;
|
||||
this.menuTop = top;
|
||||
});
|
||||
},
|
||||
// 新建菜单
|
||||
handleAddMenu(row) {
|
||||
console.log(row)
|
||||
this.$refs.basicFormRef.onOpen(null, this.pageForm.platform)
|
||||
},
|
||||
handleAddChildMenu(row) {
|
||||
this.$refs.basicFormRef.addChildMenu(row, this.pageForm.platform)
|
||||
},
|
||||
handleEdit(row) {
|
||||
// 判断子菜单的type是权限还是菜单
|
||||
if (row.type === 'menu') {
|
||||
this.$refs.basicFormRef.onOpen(row.id, this.pageForm.platform)
|
||||
} else {
|
||||
this.$refs.permissionFormRef.onOpen(row.id)
|
||||
}
|
||||
},
|
||||
handleDelete(row) {
|
||||
// 删除逻辑
|
||||
console.log(row)
|
||||
this.$confirm("此操作将删除 " + row.name, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/sys/menu/delete/" + row.id).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.loadChildByExpandedKeys()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.initTreeTable()
|
||||
},
|
||||
initTreeTable() {
|
||||
this.$axios
|
||||
.post("/platform/sys/menu/child", {
|
||||
pid: "",
|
||||
...this.pageForm
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data
|
||||
this.tableKey = +new Date()
|
||||
}
|
||||
})
|
||||
},
|
||||
loadChild: function (tree, treeNode, resolve) {
|
||||
// 在之前声明的全局变量中,增加一个key为 本条数据的id,id可替换为你数据中的任意唯一值
|
||||
this.tableTreeRefreshTool[tree.id] = {}
|
||||
// 重要!保存resolve方法,以便后续使用
|
||||
this.tableTreeRefreshTool[tree.id].resolve = resolve
|
||||
// 记录展开次数,具体作用后续介绍
|
||||
this.tableTreeRefreshTool[tree.id].expandCount = 0
|
||||
const url = "/platform/sys/menu/child"
|
||||
this.$axios.post(url, { pid: tree.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve(res.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
dropdownCommand(command) {
|
||||
//监听下拉框事件
|
||||
// if ("add" === command.type) {
|
||||
// this.$refs.basicFormRef.onOpenAdd(command.id, this.pageForm.platform)
|
||||
// }
|
||||
if ("editMenu" === command.type) {
|
||||
this.$refs.basicFormRef.onOpen(command.id, this.pageForm.platform)
|
||||
}
|
||||
if ("editData" === command.type) {
|
||||
this.$refs.permissionFormRef.onOpen(command.id)
|
||||
}
|
||||
if ("enable" === command.type || "disable" === command.type) {
|
||||
this.$axios.post("/platform/sys/menu/" + command.type + "/" + command.id).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
if ("disable" === command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" === command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if ("delete" === command.type) {
|
||||
this.$confirm("此操作将删除 " + command.name, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/sys/menu/delete/" + command.id).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.loadChildByExpandedKeys()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
handleExpandChange(row, expanded) {
|
||||
if ((row.parentId === "" || row.parentId == null) && !expanded) {
|
||||
this.expandedRowKeysRenew = []
|
||||
return
|
||||
}
|
||||
if (expanded) {
|
||||
this.expandedRowKeysRenew.push(row.id)
|
||||
} else {
|
||||
const index = this.expandedRowKeysRenew.findIndex((v) => v === row.id)
|
||||
this.expandedRowKeysRenew.splice(index, 1)
|
||||
}
|
||||
},
|
||||
loadChildByExpandedKeys() {
|
||||
if (this.expandedRowKeysRenew && this.expandedRowKeysRenew.length > 0) {
|
||||
this.expandedRowKeysRenew.forEach((v) => {
|
||||
const curr = this.tableTreeRefreshTool[v]
|
||||
// api请求
|
||||
$.get("/platform/sys/menu/child", { pid: v }).then((res) => {
|
||||
curr.resolve(res.data)
|
||||
})
|
||||
})
|
||||
} else {
|
||||
this.initTreeTable()
|
||||
}
|
||||
},
|
||||
|
||||
listModule() {
|
||||
this.$axios.post(loc() + "/listModule", { platform: this.pageForm.platform }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.moduleOptions = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
updateLocation(row) {
|
||||
const location = Number(row.location)
|
||||
if (!Number.isInteger(location) || location < 0) {
|
||||
this.$message.error("排序编码必须是非负整数")
|
||||
return
|
||||
}
|
||||
$.post("/platform/sys/menu/updateLocation", {
|
||||
id: row.id,
|
||||
location
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
if (row.parentId) {
|
||||
this.loadChildByExpandedKeys()
|
||||
} else {
|
||||
this.initTreeTable()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 点击任意处关闭菜单
|
||||
document.addEventListener('click', () => (this.menuVisible = false));
|
||||
},
|
||||
beforeDestroy() {
|
||||
document.removeEventListener('click', () => (this.menuVisible = false));
|
||||
},
|
||||
created() {
|
||||
setTimeout(() => {
|
||||
if (this.dict.type.SYS_MENU_PLATFORM) {
|
||||
this.pageForm.platform = this.dict.type.SYS_MENU_PLATFORM[0].code
|
||||
this.listModule()
|
||||
this.initTreeTable()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,677 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="ti-plus"></i>
|
||||
新建菜单
|
||||
</el-button>
|
||||
<el-button @click="openSort" size="medium" type="primary">排序</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:highlight-current-row="true"
|
||||
:key="tableKey"
|
||||
:load="loadChild"
|
||||
lazy
|
||||
row-key="id"
|
||||
:tree-props="{children: 'children', hasChildren: 'hasChildren'}"
|
||||
>
|
||||
>
|
||||
<el-table-column
|
||||
:show-overflow-tooltip="true"
|
||||
align="left"
|
||||
header-align="center"
|
||||
label="菜单名称"
|
||||
prop="name"
|
||||
width="200"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="URL" prop="href"></el-table-column>
|
||||
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="资源类型" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<i style="font-weight: bold" v-if="scope.row.type=='menu'">菜单</i>
|
||||
<i v-if="scope.row.type=='data'">数据</i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="权限标识" prop="permission"></el-table-column>
|
||||
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="启用状态" prop="disabled">
|
||||
<template slot-scope="scope">
|
||||
<i class="fa fa-circle text-danger ml5" v-if="scope.row.disabled"></i>
|
||||
<i class="fa fa-circle text-success ml5" v-if="!scope.row.disabled"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column :show-overflow-tooltip="true" align="center" header-align="center" label="操作" prop="website" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'add',id:scope.row.id,name:scope.row.name}" v-if="scope.row.type=='menu'">
|
||||
添加子菜单
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'enable',id:scope.row.id,name:scope.row.name,row:scope.row}" divided>
|
||||
启用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',id:scope.row.id,name:scope.row.name,row:scope.row}">禁用</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'editMenu',id:scope.row.id,name:scope.row.name}" divided v-if="scope.row.type=='menu'">
|
||||
修改
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'editData',id:scope.row.id,name:scope.row.name}" divided v-if="scope.row.type=='data'">
|
||||
修改
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',id:scope.row.id,name:scope.row.name}" v-if="scope.row.path!='0001'">
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="新建菜单" :visible.sync="addDialogVisible" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="parentId" label="上级菜单" label-width="80px">
|
||||
<el-cascader
|
||||
v-if="!isAddFromSub"
|
||||
:options="menuOptions"
|
||||
:props="props"
|
||||
v-model="parentMenu"
|
||||
placeholder="不选择则为顶级"
|
||||
style="width: 100%"
|
||||
></el-cascader>
|
||||
<el-input v-if="isAddFromSub" type="text" v-model="formData.parentName" :disabled="true"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="菜单名称">
|
||||
<el-input maxlength="100" placeholder="菜单名称" v-model="formData.name" auto-complete="off" tabindex="3" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="aliasName" label="菜单别名">
|
||||
<el-input maxlength="100" placeholder="菜单名称" v-model="formData.aliasName" auto-complete="off" tabindex="4" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission" label="权限标识">
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="权限标识,如 sys.manager.menu"
|
||||
v-model="formData.permission"
|
||||
auto-complete="off"
|
||||
tabindex="5"
|
||||
type="text"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="href" label="URL">
|
||||
<el-input maxlength="100" placeholder="URL" v-model="formData.href" auto-complete="off" tabindex="6" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="icon" label="图标">
|
||||
<el-input maxlength="100" placeholder="图标" v-model="formData.icon" auto-complete="off" tabindex="7" type="text"></el-input>
|
||||
<span :class="formData.icon"></span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="数据权限">
|
||||
<el-radio-group @change="formRadioChange" v-model="formData.children" size="mini">
|
||||
<el-radio label="false">无</el-radio>
|
||||
<el-radio label="true">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.children=='true'"
|
||||
v-for="(menu, index) in formData.buttons"
|
||||
:label="'权限' + (index+1)"
|
||||
:key="menu.key"
|
||||
:prop="'buttons[' + index + ']'"
|
||||
:rules="{required: true,validator: validateMenu,trigger: ['blur','change']}"
|
||||
>
|
||||
<el-row :gutter="2">
|
||||
<el-col :span="6">
|
||||
<el-input v-model="menu.name" placeholder="权限名称"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="13">
|
||||
<el-input v-model="menu.permission" placeholder="权限标识,如 sys.manager.menu.add"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button @click.prevent="formRemoveMenu(menu)" icon="el-icon-delete" size="small"></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-row v-if="formData.children=='true'" style="text-align: right; padding-right: 17px">
|
||||
<el-button size="small" @click="formAddMenu" icon="el-icon-circle-plus">添加</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="修改菜单" :visible.sync="editMenuDialogVisible" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="editMenuForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="parentId" label="上级菜单" label-width="80px">
|
||||
<el-input type="text" v-model="formData.parentName" :disabled="true"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="菜单名称">
|
||||
<el-input maxlength="100" placeholder="菜单名称" v-model="formData.name" auto-complete="off" tabindex="3" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="aliasName" label="菜单别名">
|
||||
<el-input maxlength="100" placeholder="菜单名称" v-model="formData.aliasName" auto-complete="off" tabindex="4" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission" label="权限标识">
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="权限标识,如 sys.manager.menu"
|
||||
v-model="formData.permission"
|
||||
auto-complete="off"
|
||||
tabindex="5"
|
||||
type="text"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="href" label="URL">
|
||||
<el-input maxlength="100" placeholder="URL" v-model="formData.href" auto-complete="off" tabindex="6" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="icon" label="图标">
|
||||
<el-input maxlength="100" placeholder="图标" v-model="formData.icon" auto-complete="off" tabindex="7" type="text"></el-input>
|
||||
<span :class="formData.icon"></span>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="数据权限">
|
||||
<el-radio-group @change="formRadioChange" v-model="formData.children" size="mini">
|
||||
<el-radio label="false">无</el-radio>
|
||||
<el-radio label="true">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.children=='true'"
|
||||
v-for="(menu, index) in formData.buttons"
|
||||
:label="'权限' + (index+1)"
|
||||
:key="menu.key"
|
||||
:prop="'buttons[' + index + ']'"
|
||||
:rules="{required: true,validator: validateMenu,trigger: ['blur','change']}"
|
||||
>
|
||||
<el-row :gutter="2">
|
||||
<el-col :span="6">
|
||||
<el-input v-model="menu.name" placeholder="权限名称"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="13">
|
||||
<el-input v-model="menu.permission" placeholder="权限标识,如 sys.manager.menu.add"></el-input>
|
||||
</el-col>
|
||||
<el-col :span="4">
|
||||
<el-button @click.prevent="formRemoveMenu(menu)" icon="el-icon-delete" size="small"></el-button>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<el-row v-if="formData.children=='true'" style="text-align: right; padding-right: 17px">
|
||||
<el-button size="small" @click="formAddMenu" icon="el-icon-circle-plus">添加</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editMenuDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEditMenu">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="修改权限" :visible.sync="editDataDialogVisible" width="50%">
|
||||
<el-form :model="formData2" ref="editDataForm" :rules="formRules2" size="small" label-width="80px">
|
||||
<el-form-item prop="name" label="权限名称">
|
||||
<el-input maxlength="100" placeholder="权限名称" v-model="formData2.name" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission" label="权限标识">
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="权限标识,如 sys.manager.menu.add"
|
||||
v-model="formData2.permission"
|
||||
auto-complete="off"
|
||||
tabindex="2"
|
||||
type="text"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData2.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDataDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEditData">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="菜单排序" :visible.sync="sortDialogVisible" width="50%">
|
||||
<el-tree ref="sortMenuTree" :data="sortMenuData" draggable :allow-drop="sortAllowDrop" node-key="id" :props="defaultProps">
|
||||
<span class="custom-tree-node" slot-scope="scope">
|
||||
<span>{{ scope.node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="sortDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSort">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editMenuDialogVisible: false,
|
||||
editDataDialogVisible: false,
|
||||
sortDialogVisible: false,
|
||||
isAddFromSub: false,
|
||||
tableData: [], //后台取出的数据
|
||||
tableKey: "",
|
||||
formData: {
|
||||
id: "",
|
||||
parentId: "",
|
||||
parentName: "",
|
||||
type: "menu",
|
||||
target: "data-pjax",
|
||||
children: "false",
|
||||
buttons: []
|
||||
},
|
||||
formRules: {
|
||||
name: [{ required: true, message: "菜单名称", trigger: "blur" }],
|
||||
permission: [{ required: true, message: "权限标识", trigger: ["blur", "change"] }]
|
||||
},
|
||||
formData2: {},
|
||||
formRules2: {
|
||||
name: [{ required: true, message: "权限名称", trigger: "blur" }],
|
||||
permission: [{ required: true, message: "权限标识", trigger: ["blur", "change"] }]
|
||||
},
|
||||
menuOptions: [],
|
||||
parentMenu: [],
|
||||
sortMenuData: [],
|
||||
defaultProps: {
|
||||
children: "children",
|
||||
label: "label"
|
||||
},
|
||||
props: {
|
||||
lazy: true,
|
||||
checkStrictly: true,
|
||||
multiple: false,
|
||||
lazyLoad: function (node, resolve) {
|
||||
var url = base + "/platform/sys/menu/tree"
|
||||
$.post(
|
||||
url,
|
||||
{ pid: node.value },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
resolve(data.data)
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//验证动态添加行数据
|
||||
validateMenu: function (rule, value, callback) {
|
||||
if (value.name == "") {
|
||||
callback(new Error("权限名称不能为空"))
|
||||
} else if (value.permission == "") {
|
||||
callback(new Error("权限标识不能为空"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
},
|
||||
formRadioChange: function (val) {
|
||||
this.formData.children = val
|
||||
if ("true" == val) {
|
||||
this.formData.buttons = [
|
||||
{
|
||||
name: "",
|
||||
permission: "",
|
||||
key: Date.now()
|
||||
}
|
||||
]
|
||||
} else {
|
||||
this.formData.buttons = []
|
||||
}
|
||||
},
|
||||
formRemoveMenu: function (menu) {
|
||||
var index = this.formData.buttons.indexOf(menu)
|
||||
if (index !== -1) {
|
||||
this.formData.buttons.splice(index, 1)
|
||||
}
|
||||
},
|
||||
formAddMenu: function () {
|
||||
this.formData.buttons.push({
|
||||
name: "",
|
||||
permission: "",
|
||||
key: Date.now()
|
||||
})
|
||||
},
|
||||
//排序树加载
|
||||
sortMenuLoad: function () {
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/menu/menuAll",
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.sortMenuData = data.data
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
//排序树控制不可跨级拖拽
|
||||
sortAllowDrop: function (moveNode, inNode, type) {
|
||||
if (moveNode.data.parentId == inNode.data.parentId) {
|
||||
return type == "prev"
|
||||
}
|
||||
},
|
||||
//打开排序功能
|
||||
openSort: function () {
|
||||
this.sortDialogVisible = true
|
||||
this.sortMenuLoad()
|
||||
},
|
||||
//递归获取所有的ID值
|
||||
getTreeIds: function (ids, data) {
|
||||
var self = this
|
||||
data.forEach(function (obj) {
|
||||
ids.push(obj.id)
|
||||
if (obj.children && obj.children.length > 0) {
|
||||
self.getTreeIds(ids, obj.children)
|
||||
}
|
||||
})
|
||||
},
|
||||
//提交排序后的数据
|
||||
doSort: function () {
|
||||
var self = this
|
||||
var ids = []
|
||||
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "正在提交...",
|
||||
spinner: "el-icon-loading"
|
||||
})
|
||||
|
||||
self.getTreeIds(ids, self.sortMenuData)
|
||||
$.post(
|
||||
base + "/platform/sys/menu/sortDo",
|
||||
{ ids: ids.toString() },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.sortDialogVisible = false
|
||||
self.initTreeTable()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
loading.close()
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
openAdd: function () {
|
||||
var self = this
|
||||
self.isAddFromSub = false
|
||||
self.addDialogVisible = true
|
||||
self.parentMenu = []
|
||||
self.menuOptions = []
|
||||
//表单还原为初始值
|
||||
self.formData = {
|
||||
id: "",
|
||||
parentId: "",
|
||||
parentName: "",
|
||||
type: "menu",
|
||||
target: "data-pjax",
|
||||
children: "false",
|
||||
buttons: []
|
||||
}
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
doAdd: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/menu/addDo"
|
||||
self.$refs["addForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
//表单验证通过
|
||||
if (!self.isAddFromSub) {
|
||||
//如果不是添加子菜单,则从级联选择框取父节点ID
|
||||
self.formData.parentId = self.parentMenu[self.parentMenu.length - 1] || ""
|
||||
}
|
||||
$.post(
|
||||
url,
|
||||
{
|
||||
menu: JSON.stringify(self.formData),
|
||||
buttons: JSON.stringify(self.formData.buttons)
|
||||
},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.addDialogVisible = false
|
||||
self.initTreeTable()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
doEditMenu: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/menu/editMenuDo"
|
||||
self.$refs["editMenuForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
//表单验证通过
|
||||
$.post(
|
||||
url,
|
||||
{
|
||||
menu: JSON.stringify(self.formData),
|
||||
buttons: JSON.stringify(self.formData.buttons)
|
||||
},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.editMenuDialogVisible = false
|
||||
self.initTreeTable()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
doEditData: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/menu/editDataDo"
|
||||
self.$refs["editDataForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
//表单验证通过
|
||||
$.post(
|
||||
url,
|
||||
self.formData2,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.editDataDialogVisible = false
|
||||
self.initTreeTable()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
initTreeTable: function () {
|
||||
var self = this
|
||||
var url = base + "/platform/sys/menu/child"
|
||||
$.post(
|
||||
url,
|
||||
{ pid: "" },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data
|
||||
self.tableKey = +new Date()
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
loadChild: function (tree, treeNode, resolve) {
|
||||
var url = base + "/platform/sys/menu/child"
|
||||
$.post(
|
||||
url,
|
||||
{ pid: tree.id },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
resolve(data.data)
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
dropdownCommand: function (command) {
|
||||
//监听下拉框事件
|
||||
var self = this
|
||||
if ("add" == command.type) {
|
||||
self.openAdd()
|
||||
self.formData.parentId = command.id
|
||||
self.formData.parentName = command.name
|
||||
self.isAddFromSub = true
|
||||
}
|
||||
if ("editMenu" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/menu/editMenu/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.formData = data.data //加载后台表单数据
|
||||
self.editMenuDialogVisible = true //打开编辑窗口
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("editData" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/menu/editData/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.formData2 = data.data //加载后台表单数据
|
||||
self.editDataDialogVisible = true //打开编辑窗口
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("enable" == command.type || "disable" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/menu/" + command.type + "/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
if ("disable" == command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" == command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("delete" == command.type) {
|
||||
self.$confirm("此操作将删除 " + command.name, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/menu/delete/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
// self.initTreeTable();
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.initTreeTable()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,59 @@
|
||||
const SYS_MENU_PERMISSION_FORM_COMPONENT = {
|
||||
template: `
|
||||
<el-dialog title="修改权限" :visible.sync="editDataDialogVisible" width="50%">
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="name" label="权限名称">
|
||||
<el-input maxlength="100" placeholder="权限名称" v-model="formData.name" auto-complete="off"
|
||||
tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission" label="权限标识">
|
||||
<el-input
|
||||
maxlength="100"
|
||||
placeholder="权限标识,如 sys.manager.menu.add"
|
||||
v-model="formData.permission"
|
||||
auto-complete="off"
|
||||
tabindex="2"
|
||||
type="text"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDataDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEditData">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
editDataDialogVisible: false,
|
||||
formData: {},
|
||||
formRules: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(id) {
|
||||
this.editDataDialogVisible = true
|
||||
this.$axios.post("/platform/sys/menu/editData/" + id, {}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
doEditData() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$axios.post("/platform/sys/menu/editDataDo", this.formData).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.editDataDialogVisible = false
|
||||
this.$emit("load-child", null)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
const SYS_MENU_RECOMMEND_SETTING_COMPONENT = {
|
||||
template: `
|
||||
<el-dialog title="推荐配置" :visible.sync="dialogVisible" width="50%">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-input
|
||||
v-model="menuKeyword"
|
||||
clearable
|
||||
prefix-icon="el-icon-search"
|
||||
placeholder="请输入菜单名称搜索"
|
||||
style="margin-bottom: 10px">
|
||||
</el-input>
|
||||
<el-tree ref="menuTree"
|
||||
style="max-height: 450px;overflow-y: auto"
|
||||
:data="menuData"
|
||||
:check-on-click-node="true"
|
||||
:filter-node-method="filterMenuNode"
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
@check-change="checkChange"
|
||||
:props="defaultProps">
|
||||
<span class="custom-tree-node" slot-scope="scope">
|
||||
<span>{{ scope.node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-table :data="checkedNodes" max-height="500px">
|
||||
<el-table-column type="index"></el-table-column>
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="图标" prop="icon">
|
||||
<template scope="{row}">
|
||||
<svg-icon :name="row.icon" v-if="platform==='PC'"></svg-icon>
|
||||
<img v-else :src="row.icon" alt="" style="width: 30px;height: 30px">
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="submit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
platform: null,
|
||||
dialogVisible: false,
|
||||
menuData: [],
|
||||
menuKeyword: '',
|
||||
defaultProps: {
|
||||
children: "children",
|
||||
label: "label"
|
||||
},
|
||||
|
||||
checkedNodes: [],
|
||||
checkedKeys: [],
|
||||
|
||||
type: '',
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
menuKeyword(value) {
|
||||
if (this.$refs.menuTree) {
|
||||
this.$refs.menuTree.filter(value)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(platform, type) {
|
||||
this.platform = platform
|
||||
this.type = type
|
||||
this.checkedKeys = []
|
||||
this.$set(this, 'menuKeyword', '')
|
||||
this.dialogVisible = true
|
||||
this.listTree()
|
||||
},
|
||||
|
||||
// 仅过滤左侧菜单树的显示,不修改节点勾选状态,保证搜索前的选择仍能提交。
|
||||
filterMenuNode(value, data) {
|
||||
if (!value) {
|
||||
return true
|
||||
}
|
||||
return (data.label || data.name || '').indexOf(value) !== -1
|
||||
},
|
||||
|
||||
listTree() {
|
||||
this.$axios.post("/platform/sys/menu/menuAll", { platform: this.platform }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.setTreeDisabled(res.data)
|
||||
this.menuData = res.data
|
||||
console.log(this.checkedKeys)
|
||||
//设置选中的节点
|
||||
this.$refs.menuTree.setCheckedKeys(this.checkedKeys)
|
||||
//获取选中的节点
|
||||
this.$nextTick(() => {
|
||||
this.checkedNodes = this.$refs.menuTree.getCheckedNodes(true, false)
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
setTreeDisabled(data) {
|
||||
data.forEach((item) => {
|
||||
if ((this.type === 'service' && item.isRecommendService) || (this.type === 'app' && item.isRecommendApp)) {
|
||||
this.checkedKeys.push(item.id)
|
||||
}
|
||||
if (!item.href && item.type === "menu" && this.type === 'service') {
|
||||
item.disabled = true
|
||||
}
|
||||
if (this.type === 'app') {
|
||||
delete item.children
|
||||
}
|
||||
if (item.children && item.children.length) {
|
||||
this.setTreeDisabled(item.children)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
checkChange(currObj, isChecked) {
|
||||
this.checkedNodes = this.$refs.menuTree.getCheckedNodes(true, false)
|
||||
},
|
||||
|
||||
submit() {
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
// 提交时从完整菜单树读取选中节点,搜索隐藏的已选节点也必须保留。
|
||||
const selectedNodes = this.$refs.menuTree
|
||||
? this.$refs.menuTree.getCheckedNodes(true, false)
|
||||
: this.checkedNodes
|
||||
this.$axios
|
||||
.post("/platform/sys/menu/updateRecommendSetting", {
|
||||
menuIds: JSON.stringify(selectedNodes.map((item) => item.id)),
|
||||
platform: this.platform,
|
||||
type: this.type
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.checkedKeys = []
|
||||
this.listTree()
|
||||
this.$message.success(resp.msg)
|
||||
this.$emit("refresh", null)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
const SYS_MENU_SORT_COMPONENT = {
|
||||
template: `
|
||||
<el-dialog title="菜单排序" :visible.sync="sortDialogVisible" width="50%">
|
||||
<el-tree ref="sortMenuTree" :data="sortMenuData" draggable :allow-drop="sortAllowDrop" node-key="id"
|
||||
:props="defaultProps">
|
||||
<span class="custom-tree-node" slot-scope="scope">
|
||||
<span>{{ scope.node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="sortDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSort">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
sortDialogVisible: false,
|
||||
sortMenuData: [],
|
||||
defaultProps: {
|
||||
children: "children",
|
||||
label: "label"
|
||||
},
|
||||
platform: 'PC'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(platform) {
|
||||
this.platform = platform
|
||||
this.sortDialogVisible = true
|
||||
this.$axios.post("/platform/sys/menu/menuAll", { platform }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.sortMenuData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
//排序树控制不可跨级拖拽
|
||||
sortAllowDrop: function (moveNode, inNode, type) {
|
||||
if (moveNode.data.parentId == inNode.data.parentId) {
|
||||
return type == "prev"
|
||||
}
|
||||
},
|
||||
//提交排序后的数据
|
||||
doSort() {
|
||||
const ids = []
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "正在提交...",
|
||||
spinner: "el-icon-loading"
|
||||
})
|
||||
this.getTreeIds(ids, this.sortMenuData)
|
||||
this.$axios
|
||||
.post("/platform/sys/menu/sortDo", { ids: ids.toString(), platform: this.platform })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.sortDialogVisible = false
|
||||
this.$emit("refresh", null)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
},
|
||||
|
||||
//递归获取所有的ID值
|
||||
getTreeIds(ids, data) {
|
||||
const self = this
|
||||
data.forEach(function (obj) {
|
||||
ids.push(obj.id)
|
||||
if (obj.children && obj.children.length > 0) {
|
||||
self.getTreeIds(ids, obj.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app">
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="平台">
|
||||
<el-select v-model="pageForm.platform" :clearable="false" @change="doSearch">
|
||||
<el-option v-for="item in dict.type.SYS_MENU_PLATFORM" :value="item.code" :key="item.value" :label="item.label"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="名称">
|
||||
<el-input v-model="pageForm.searchKeyword" placeholder="请输入名称" clearable></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button size="small" type="primary" @click="openAdd">添加模块</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" align="center" header-align="center">
|
||||
<el-table-column
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:key="column.prop"
|
||||
:sortable="column.sortable"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns"
|
||||
>
|
||||
<template scope="{row}" v-if="column.prop=='icon'">
|
||||
<el-image lazy :src="row.icon" style="height: 30px; width: 30px" fit="cover">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop=='faIcon'">
|
||||
<i :class="row.faIcon"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200px">
|
||||
<template scope="{row}">
|
||||
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
|
||||
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog :visible.sync="formDialogVisible" :title="formData.id ? '编辑模块' : '添加模块'">
|
||||
<el-form :model="formData" :rules="formRules" label-width="120px" ref="form">
|
||||
<el-form-item label="所属平台" prop="platform">
|
||||
<el-select v-model="formData.platform" :clearable="false" @change="doSearch" placeholder="请选择平台">
|
||||
<el-option v-for="item in dict.type.SYS_MENU_PLATFORM" :value="item.code" :key="item.value" :label="item.label"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="模块名称" prop="name">
|
||||
<el-input v-model="formData.name" maxlength="8"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="模块图片" prop="icon">
|
||||
<file-upload
|
||||
:value.sync="formData.icon"
|
||||
:upload_number="1"
|
||||
upload_mode="image"
|
||||
upload_result_category="interval"
|
||||
upload_result_type="url"
|
||||
></file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="图标" prop="faIcon">
|
||||
<el-input maxlength="100" placeholder="图标" v-model="formData.faIcon"></el-input>
|
||||
<i :class="formData.faIcon" v-if="formData.faIcon"></i>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序编号" prop="sortNum">
|
||||
<el-input-number v-model="formData.sortNum" :min="1" :precision="0"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="formDialogVisible=false">取消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确定</el-button>
|
||||
</el-row>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
dicts: ["SYS_MENU_PLATFORM"],
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
formDialogVisible: false,
|
||||
formData: {
|
||||
id: null,
|
||||
name: null,
|
||||
icon: null,
|
||||
sortNum: null
|
||||
},
|
||||
formRules: {
|
||||
platform: [{ required: true, message: "请填写", trigger: ["blur", "change"] }],
|
||||
name: [{ required: true, message: "请填写", trigger: ["blur", "change"] }],
|
||||
icon: [{ required: true, message: "请填写", trigger: ["blur", "change"] }],
|
||||
sortNum: [{ required: true, message: "请输入", trigger: ["blur", "change"] }],
|
||||
faIcon: [{ required: true, message: "请填写", trigger: ["blur", "change"] }]
|
||||
},
|
||||
firstMenus: [],
|
||||
tableColumns: [
|
||||
{ label: "排序编号", prop: "sortNum", sortable: true },
|
||||
{ label: "所属平台", prop: "platform", sortable: true },
|
||||
{ label: "模块名称", prop: "name" },
|
||||
{ label: "模块图标", prop: "icon" },
|
||||
{ label: "模块图标", prop: "faIcon" }
|
||||
],
|
||||
pageForm: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"formData.icon"(val) {
|
||||
this.$refs.form.validateField("icon", (errMsg) => {
|
||||
console.log(errMsg)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async openAdd() {
|
||||
if (this.$refs.form) {
|
||||
this.$refs.form.resetFields()
|
||||
}
|
||||
this.formData = {
|
||||
sortNum: this.tableData.length + 1
|
||||
}
|
||||
this.formDialogVisible = true
|
||||
},
|
||||
openEdit(row) {
|
||||
this.formDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = { ...row }
|
||||
})
|
||||
},
|
||||
|
||||
async doSubmit() {
|
||||
const valid = await this.$refs.form.validate()
|
||||
if (!valid) return
|
||||
const { id } = this.formData
|
||||
const url = loc() + (id ? "/update" : "/insert")
|
||||
const resp = await this.$axios.post(url, this.formData)
|
||||
if (resp.code === 0) {
|
||||
this.pageData()
|
||||
this.formDialogVisible = false
|
||||
this.$message.success(resp.msg)
|
||||
}
|
||||
},
|
||||
|
||||
doDelete(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
this.$axios.post(loc() + "/delete", { id }).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
setTimeout(() => {
|
||||
if (this.dict.type.SYS_MENU_PLATFORM) {
|
||||
this.pageForm.platform = this.dict.type.SYS_MENU_PLATFORM[0].code
|
||||
this.pageData()
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,182 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.el-msg-active {
|
||||
background-color: #546478;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.note {
|
||||
width: 100%;
|
||||
height: 320px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="消息类型:">
|
||||
<el-select clearable placeholder="请选择消息类型" v-model="pageForm.searchType" style="width: 100%" @change="doSearch">
|
||||
<el-option label="全部类型消息" value=""></el-option>
|
||||
<el-option label="系统消息" value="system"></el-option>
|
||||
<el-option label="用户消息" value="user"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool label="消息列表">
|
||||
<el-button size="small" type="primary" icon="el-icon-plus" @click="openAdd">发送消息</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="消息类型" prop="type" sortable>
|
||||
<template slot-scope="scope">
|
||||
<span v-if="'system'==scope.row.type">系统消息</span>
|
||||
<span v-if="'user'==scope.row.type">用户消息</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="标题" prop="title"></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="消息内容" prop="note">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" @click="openViewNote(scope.row)">查看</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="总接收用户" prop="all_num">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" @click="viewUserList('all',scope.row.id)">{{scope.row.all_num}}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="未读用户" prop="unread_num">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="primary" @click="viewUserList('unread',scope.row.id)">{{scope.row.unread_num}}</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column header-align="center" label="发送时间" prop="createdAt" sortable>
|
||||
<template slot-scope="scope">{{$moment(scope.row.createdAt).format("YYYY-MM-DD HH:mm:ss")}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column header-align="center" label="发送人" prop="createdAt" sortable>
|
||||
<template slot-scope="scope">{{scope.row.username}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="消息状态" prop="delFlag" sortable>
|
||||
<template slot-scope="scope">
|
||||
<el-tag size="mini" type="error" v-if="scope.row.delFlag">已删除</el-tag>
|
||||
<el-tag size="mini" type="success" v-if="!scope.row.delFlag">已发送</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-button size="mini" type="danger" @click="deleteDo(scope.row.id, scope.row.title)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
<template #edit>
|
||||
<msg-form ref="msgForm" @back="back" :union-list="unionList" :unit-list="unitList" @parent-do-search="doSearch"></msg-form>
|
||||
</template>
|
||||
</guava>
|
||||
<view-user ref="viewUser" :type="type" :id="id" :union-list="unionList" :unit-list="unitList"></view-user>
|
||||
|
||||
<el-dialog title="消息内容" :visible.sync="noteVisible" width="50%">
|
||||
<div class="note" v-html="nodeData.note">
|
||||
</div>
|
||||
<file-preview complete_result :files="nodeData.files"></file-preview>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("msgManage/msgManageForm.js"){}#-->
|
||||
<!--#include("msgManage/viewUser.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
"msg-form": MSG_FORM_TEMPLATE,
|
||||
"view-user": VIEW_USER_TEMPLATE
|
||||
},
|
||||
data: function () {
|
||||
return {
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
searchType: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
type: "",
|
||||
id: "",
|
||||
unionList: [],
|
||||
unitList: [],
|
||||
noteVisible: false,
|
||||
nodeData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openViewNote(row) {
|
||||
this.noteVisible = true
|
||||
this.nodeData = {
|
||||
note: row.note,
|
||||
files: row.files
|
||||
}
|
||||
},
|
||||
|
||||
back() {
|
||||
this.$refs.guava.index()
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
async pageData() {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/data", this.pageForm)
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.$refs.guava.edit()
|
||||
this.$nextTick(() => {
|
||||
this.$refs.msgForm.initAdd()
|
||||
})
|
||||
},
|
||||
viewUserList(type, id) {
|
||||
this.type = type
|
||||
this.id = id
|
||||
this.$refs.viewUser.open()
|
||||
},
|
||||
deleteDo(id, title) {
|
||||
this.$confirm("您确定要删除" + title + "吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/delete/" + id)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created: async function () {
|
||||
this.unitList = await this.$businessTool.listUnit()
|
||||
this.unionList = await this.$businessTool.listUnion()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,263 @@
|
||||
const MSG_FORM_TEMPLATE = {
|
||||
template: `
|
||||
<div>
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" label-width="120px">
|
||||
<el-form-item prop="title" label="标题">
|
||||
<el-input maxlength="255" placeholder="请输入标题" v-model="formData.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="url" label="链接地址">
|
||||
<el-input placeholder="请输入链接地址" v-model="formData.url"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="needBack" label="是否需要反馈">
|
||||
<el-radio-group v-model="formData.needBack" size="small">
|
||||
<el-radio border :label="true">是</el-radio>
|
||||
<el-radio border :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="消息类型">
|
||||
<el-radio-group v-model="formData.type" size="small">
|
||||
<!-- <el-radio border label="system">系统消息</el-radio>-->
|
||||
<el-radio border label="user">用户消息</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户列表" v-if="'user'==formData.type">
|
||||
<el-row class="mb10">
|
||||
<el-button icon="el-icon-search" @click="openSelect" size="small">选择用户</el-button>
|
||||
<el-button icon="el-icon-delete" @click="clearSelect" type="danger" size="small">清空用户</el-button>
|
||||
</el-row>
|
||||
<el-table :data="userTableData" style="width: 100%" size="mini" max-height="500">
|
||||
<el-table-column fixed prop="loginname" label="工号" width="150"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" width="120"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" width="50"></el-table-column>
|
||||
<el-table-column prop="userState" label="在职状态" width="120"></el-table-column>
|
||||
<el-table-column prop="personType" label="人员类型" width="120"></el-table-column>
|
||||
<el-table-column prop="mobile" label="手机" width="120"></el-table-column>
|
||||
<el-table-column prop="unitName" label="所属单位" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="unionName" label="所属工会" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
<p style="color: var(--color-primary)">总计:{{userTableData.length}}人</p>
|
||||
</el-form-item>
|
||||
<el-form-item prop="note" label="消息内容">
|
||||
<text-editor v-model="formData.note"></text-editor>
|
||||
</el-form-item>
|
||||
<el-form-item prop="files" label="消息附件">
|
||||
<file-upload
|
||||
:value.sync="formData.files"
|
||||
:upload_number="5"
|
||||
upload_result_category="array"
|
||||
complete_result
|
||||
></file-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-row class="mt20" justify="end" type="flex">
|
||||
<el-button @click="back">取消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确定</el-button>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-dialog title="选择用户" :visible.sync="selectDialogVisible" width="65%" append-to-body top="2%">
|
||||
<div class="btn-group tool-button">
|
||||
<el-input placeholder="请输入内容" v-model="pageForm.searchKeyword" @keyup.enter.native="doSearch" style="width: 300px">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width: 80px">
|
||||
<el-option label="工号" value="u.loginname"></el-option>
|
||||
<el-option label="姓名" value="u.username"></el-option>
|
||||
<el-option label="手机" value="u.mobile"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
<el-select v-model="pageForm.sex" placeholder="性别" clearable>
|
||||
<el-option label="男" value="男"></el-option>
|
||||
<el-option label="女" value="女"></el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select v-model="pageForm.unionId" @change="flushUnits" @clear="flushUnits" placeholder="请选择所属工会" filterable clearable>
|
||||
<el-option v-for="item in unionList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-select v-model="pageForm.unitId" placeholder="请选择所属单位" filterable clearable>
|
||||
<el-option v-for="item in unitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-select v-model="pageForm.roleId" placeholder="请选择角色" filterable clearable>
|
||||
<el-option v-for="item in roleList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-button icon="el-icon-search" @click="doSearch" type="primary"></el-button>
|
||||
</div>
|
||||
<div class="left-span-label mt20">人员列表</div>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
@selection-change="handleSelectionChange"
|
||||
row-key="id"
|
||||
ref="tableRef"
|
||||
>
|
||||
<el-table-column type="selection" reserve-selection width="35"></el-table-column>
|
||||
<el-table-column fixed prop="loginname" label="工号" width="150"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" width="120"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" width="50"></el-table-column>
|
||||
<el-table-column prop="userState" label="在职状态" width="120"></el-table-column>
|
||||
<el-table-column prop="personType" label="人员类型" width="120"></el-table-column>
|
||||
<el-table-column prop="mobile" label="手机" width="120"></el-table-column>
|
||||
<el-table-column prop="unitName" label="所属单位" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="unionName" label="所属工会" show-overflow-tooltip></el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="selectDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSelect">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
unionList: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
unitList: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
roleList: [],
|
||||
userTableData: [],
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
searchName: "u.username",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
formData: {},
|
||||
formRules: {
|
||||
title: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
note: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
needBack: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
type: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
},
|
||||
selectDialogVisible: false,
|
||||
selectUsers: []
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async flushUnits() {
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.unitList = []
|
||||
if (this.pageForm.unionId) {
|
||||
this.$businessTool.listUnit(this.pageForm.unionId).then((data) => {
|
||||
this.unitList = data
|
||||
})
|
||||
}
|
||||
this.doSearch()
|
||||
},
|
||||
back() {
|
||||
this.$emit("back")
|
||||
},
|
||||
parentDoSearch() {
|
||||
this.$emit("parent-do-search")
|
||||
},
|
||||
doAdd() {
|
||||
this.$refs["addForm"].validate((valid) => {
|
||||
if (valid) {
|
||||
const loginNames = this.userTableData.map((u) => u.loginname)
|
||||
if (loginNames.length === 0 && this.formData.type === "user") {
|
||||
this.$message.warning("请选择用户")
|
||||
return false
|
||||
}
|
||||
|
||||
if(loginNames.length > 100){
|
||||
this.$message.warning("单条消息最多发送100人,请分批次发送。")
|
||||
return false
|
||||
}
|
||||
|
||||
this.$confirm('您确定要发送吗, 是否继续?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(()=>{
|
||||
this.$axios
|
||||
.post("/platform/sys/msg/addDo", {
|
||||
msg: JSON.stringify(this.formData),
|
||||
users: JSON.stringify(loginNames)
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.parentDoSearch()
|
||||
this.back()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange(val) {
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.selectUsers = val
|
||||
},
|
||||
initAdd() {
|
||||
this.formData = {
|
||||
id: "",
|
||||
type: "user",
|
||||
needBack: false
|
||||
}
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
openSelect: function () {
|
||||
this.selectDialogVisible = true
|
||||
this.pageData()
|
||||
},
|
||||
clearSelect() {
|
||||
this.userTableData = []
|
||||
if(this.$refs.tableRef){
|
||||
this.$refs.tableRef.clearSelection()
|
||||
}
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post("/platform/sys/msg/user_data", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
doSelect() {
|
||||
const self = this
|
||||
console.log(this.selectUsers)
|
||||
console.log(this.userTableData)
|
||||
const tableLoginNames = this.userTableData.map(v=>v.loginname)
|
||||
this.selectUsers.forEach(function (o) {
|
||||
if(!tableLoginNames.includes(o.loginname)){
|
||||
self.userTableData.push(o)
|
||||
}
|
||||
})
|
||||
if(this.$refs.tableRef){
|
||||
this.$refs.tableRef.clearSelection()
|
||||
}
|
||||
this.selectDialogVisible = false
|
||||
},
|
||||
async getRoleList() {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/getRoleList")
|
||||
return resp.data
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.roleList = await this.getRoleList()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
const VIEW_USER_TEMPLATE = {
|
||||
template: `
|
||||
<div>
|
||||
<el-dialog :title="viewUserTitle" :visible.sync="viewDialogVisible" width="65%" top="2%">
|
||||
<div class="btn-group tool-button">
|
||||
<el-input placeholder="请输入内容" v-model="pageForm.searchKeyword" size="small" @keyup.enter.native="doSearch" style="width: 300px">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width: 80px">
|
||||
<el-option label="工号" value="u.loginname"></el-option>
|
||||
<el-option label="姓名" value="u.username"></el-option>
|
||||
<el-option label="手机" value="u.mobile"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
<el-select v-model="pageForm.unionId" @change="flushUnits" @clear="flushUnits" placeholder="请选择所属工会" size="small" filterable clearable>
|
||||
<el-option v-for="item in unionList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-select v-model="pageForm.unitId" placeholder="请选择所属单位" size="small" filterable clearable>
|
||||
<el-option v-for="item in unitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-button icon="el-icon-search" @click="doSearch" size="small" type="primary"></el-button>
|
||||
</div>
|
||||
<div class="left-span-label mt20">{{viewUserTitle}}列表</div>
|
||||
<el-table
|
||||
:data="tableData"
|
||||
style="width: 100%; margin-top: 10px; margin-bottom: 10px"
|
||||
size="small"
|
||||
>
|
||||
<el-table-column fixed prop="loginname" label="工号" width="150"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" width="120"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" width="120"></el-table-column>
|
||||
<el-table-column prop="mobile" label="联系方式"></el-table-column>
|
||||
<el-table-column prop="unitName" label="所属单位"></el-table-column>
|
||||
<el-table-column prop="unionName" label="所属工会"></el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="viewDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
unionList: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
unitList: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
viewUserTitle: "",
|
||||
viewDialogVisible: false,
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
searchName: "u.username",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
async flushUnits() {
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.unitList = []
|
||||
if (this.pageForm.unionId) {
|
||||
this.$businessTool.listUnit(this.pageForm.unionId).then((data) => {
|
||||
this.unitList = data
|
||||
})
|
||||
}
|
||||
this.doSearch()
|
||||
},
|
||||
open() {
|
||||
this.viewDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
if ("all" === this.type) {
|
||||
this.viewUserTitle = "全部用户"
|
||||
}
|
||||
if ("unread" === this.type) {
|
||||
this.viewUserTitle = "未读用户"
|
||||
}
|
||||
this.pageForm.type = this.type
|
||||
this.pageForm.id = this.id
|
||||
this.doSearch()
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange(val) {
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData() {
|
||||
this.$axios.post("/platform/sys/msg/user_view_data", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
async created() {}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style></style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="消息类型">
|
||||
<el-select clearable placeholder="请选择消息类型" v-model="pageForm.searchType" style="width: 100%" @change="getMsg">
|
||||
<el-option label="全部类型消息" value="all"></el-option>
|
||||
<el-option label="系统消息" value="system"></el-option>
|
||||
<el-option label="用户消息" value="user"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="消息标题">
|
||||
<el-select placeholder="请选择消息标题" v-model="pageForm.msgId" style="width: 100%" @change="doSearch">
|
||||
<el-option v-for="item in msgOptions" :key="item.id" :label="item.title" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="状态">
|
||||
<el-select placeholder="请选择状态" v-model="pageForm.readStatus" style="width: 100%" @change="doSearch">
|
||||
<el-option label="全部" :value="null"></el-option>
|
||||
<el-option label="未阅读" :value="0"></el-option>
|
||||
<el-option label="已阅读未反馈" :value="1"></el-option>
|
||||
<el-option label="已阅读无需反馈" :value="2"></el-option>
|
||||
<el-option label="已阅读已反馈" :value="3"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="姓名/工号">
|
||||
<el-input placeholder="请输入姓名或工号" v-model="pageForm.searchKeyword" @keyup.enter.native="doSearch" clearable>
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width: 80px">
|
||||
<el-option label="工号" value="mu.loginname"></el-option>
|
||||
<el-option label="姓名" value="mu.userName"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属工会">
|
||||
<el-select v-model="pageForm.unionId" @change="flushUnits" @clear="flushUnits" placeholder="请选择所属工会" filterable clearable>
|
||||
<el-option v-for="item in unionList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="所属单位">
|
||||
<el-select v-model="pageForm.unitId" placeholder="请选择所属单位" filterable clearable @change="doSearch">
|
||||
<el-option v-for="item in unitList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<table-tool label="人员列表">
|
||||
<el-button type="primary" @click="exportMultipleAsZip" size="small">导出压缩包</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" size="small" style="width: 100%">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||
<el-table-column prop="userName" label="姓名"></el-table-column>
|
||||
<el-table-column prop="loginname" label="工号"></el-table-column>
|
||||
<!-- <el-table-column prop="sex" label="性别"></el-table-column>-->
|
||||
<!-- <el-table-column prop="mobile" label="联系方式"></el-table-column>-->
|
||||
<el-table-column prop="unitName" label="所属单位"></el-table-column>
|
||||
<el-table-column prop="unionName" label="所属工会"></el-table-column>
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template v-slot="scope">
|
||||
<el-tag size="mini" v-if="0 === scope.row.status">未阅读</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="1 === scope.row.status && scope.row.needBack">已阅读未反馈</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="1 === scope.row.status && !scope.row.needBack">已阅读无需反馈</el-tag>
|
||||
<el-tag size="mini" v-else-if="2 === scope.row.status">已阅读已反馈</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="backFiles" label="反馈附件">
|
||||
<template v-slot="scope">
|
||||
<el-link type="primary" v-if="scope.row.backFiles.length > 0" @click="viewFiles(scope.row)">点击查看</el-link>
|
||||
<el-link type="info" v-else>暂无</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
<el-dialog :visible.sync="backDialogVisible" title="反馈附件" width="40%" top="2%">
|
||||
|
||||
<el-dialog :visible.sync="backDialogVisible" title="反馈附件" width="40%" top="2%">
|
||||
<el-form v-model="backFormData" ref="backFormRef" label-width="80px">
|
||||
<el-form-item label="反馈附件" prop="files">
|
||||
<div style="width: 100%; text-align: center">
|
||||
<file-preview :files="backFiles" complete_result></file-preview>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="反馈内容" prop="backText">
|
||||
<el-input v-model="backFormData.backText" type="textarea" :rows="4" max="500" readonly></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
backDialogVisible: false,
|
||||
backFiles: [],
|
||||
backFormData: {},
|
||||
|
||||
msgOptions: [],
|
||||
pageForm: {
|
||||
searchType: "all",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
searchName: "mu.userName",
|
||||
readType: null
|
||||
},
|
||||
unionList: [],
|
||||
unitList: [],
|
||||
}
|
||||
},
|
||||
components: {},
|
||||
methods: {
|
||||
exportMultipleAsZip() {
|
||||
this.$downLoad(loc() + "/exportMultipleAsZip", this.pageForm)
|
||||
},
|
||||
async flushUnits() {
|
||||
this.$set(this.pageForm, "unitId", null)
|
||||
this.unitList = []
|
||||
if (this.pageForm.unionId) {
|
||||
this.$businessTool.listUnit(this.pageForm.unionId).then((data) => {
|
||||
this.unitList = data
|
||||
})
|
||||
}
|
||||
this.doSearch()
|
||||
},
|
||||
viewFiles(row) {
|
||||
this.backFormData = {
|
||||
...row
|
||||
}
|
||||
this.backFiles = row.backFiles
|
||||
this.backDialogVisible = true
|
||||
},
|
||||
async getMsg() {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/summary/getMsg", { searchType: this.pageForm.searchType })
|
||||
this.msgOptions = resp.data
|
||||
if (this.msgOptions && this.msgOptions.length > 0) {
|
||||
this.pageForm.msgId = this.msgOptions[0].id
|
||||
}
|
||||
},
|
||||
async pageData() {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/summary/pageData", this.pageForm)
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.tableData.forEach((item) => {
|
||||
item.backFiles = item.backFiles ? JSON.parse(item.backFiles) : []
|
||||
})
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.getMsg()
|
||||
const isAdmin = this.$auth.hasRoleOr(["SYSADMIN", "SCHOOL_UNION_ADMIN"])
|
||||
if (isAdmin) {
|
||||
this.$businessTool.listUnion(this.pageForm.unionId).then((data) => {
|
||||
this.unionList = data
|
||||
})
|
||||
} else {
|
||||
this.$businessTool.listUnion(this.$store.state.user.union.id).then((data) => {
|
||||
this.unionList = data
|
||||
})
|
||||
}
|
||||
this.unitList = await this.$businessTool.listUnit()
|
||||
await this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,35 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<header class="header navbar bg-white shadow">
|
||||
<div class="btn-group tool-button m5">
|
||||
<a class="el-button el-button--default el-button--medium" href="${base}/platform/sys/msg/user/all" data-pjax>
|
||||
<i class="ti-angle-left"></i>
|
||||
返回
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrap">
|
||||
<div class="wrapper" style="min-height: 500px">
|
||||
<section class="panel">
|
||||
<div class="row mb10">
|
||||
<div class="col-lg-12">
|
||||
<!--#if(!isEmpty(obj)){#-->
|
||||
<div style="text-align: center">
|
||||
<h4>${obj.title!}</h4>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 14px; line-height: 24px">${@date.getDate(obj.sendAt)}</div>
|
||||
<div style="margin-top: 16px !important; border-bottom: 1px solid #ddd"></div>
|
||||
<div style="margin-top: 16px !important">${obj.note!}</div>
|
||||
<!--#}else{#-->
|
||||
消息不存在,或您没有阅读权限!
|
||||
<!--#}#-->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,35 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<header class="header navbar bg-white shadow">
|
||||
<div class="btn-group tool-button m5">
|
||||
<a class="el-button el-button--default el-button--medium" href="${base}/platform/sys/msg/user/read" data-pjax>
|
||||
<i class="ti-angle-left"></i>
|
||||
返回
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrap">
|
||||
<div class="wrapper" style="min-height: 500px">
|
||||
<section class="panel">
|
||||
<div class="row mb10">
|
||||
<div class="col-lg-12">
|
||||
<!--#if(!isEmpty(obj)){#-->
|
||||
<div style="text-align: center">
|
||||
<h4>${obj.title!}</h4>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 14px; line-height: 24px">${@date.getDate(obj.sendAt)}</div>
|
||||
<div style="margin-top: 16px !important; border-bottom: 1px solid #ddd"></div>
|
||||
<div style="margin-top: 16px !important">${obj.note!}</div>
|
||||
<!--#}else{#-->
|
||||
消息不存在,或您没有阅读权限!
|
||||
<!--#}#-->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,35 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<header class="header navbar bg-white shadow">
|
||||
<div class="btn-group tool-button m5">
|
||||
<a class="el-button el-button--default el-button--medium" href="${base}/platform/sys/msg/user/unread" data-pjax>
|
||||
<i class="ti-angle-left"></i>
|
||||
返回
|
||||
</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="content-wrap">
|
||||
<div class="wrapper" style="min-height: 500px">
|
||||
<section class="panel">
|
||||
<div class="row mb10">
|
||||
<div class="col-lg-12">
|
||||
<!--#if(!isEmpty(obj)){#-->
|
||||
<div style="text-align: center">
|
||||
<h4>${obj.title!}</h4>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 14px; line-height: 24px">${@date.getDate(obj.sendAt)}</div>
|
||||
<div style="margin-top: 16px !important; border-bottom: 1px solid #ddd"></div>
|
||||
<div style="margin-top: 16px !important">${obj.note!}</div>
|
||||
<!--#}else{#-->
|
||||
消息不存在,或您没有阅读权限!
|
||||
<!--#}#-->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,202 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.el-msg-active {
|
||||
background-color: #546478;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="消息类型:">
|
||||
<el-select clearable placeholder="请选择消息类型" v-model="pageForm.searchType" style="width: 100%" @change="doSearch">
|
||||
<el-option label="全部类型消息" value="all"></el-option>
|
||||
<el-option label="系统消息" value="system"></el-option>
|
||||
<el-option label="用户消息" value="user"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool label="消息列表">
|
||||
<el-radio-group v-model="pageForm.status" @change="doSearch" size="small">
|
||||
<el-radio-button label="all">全部消息</el-radio-button>
|
||||
<el-radio-button label="read">已读消息</el-radio-button>
|
||||
<el-radio-button label="unread">未读消息</el-radio-button>
|
||||
</el-radio-group>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" style="width: 100%">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="60"></el-table-column>
|
||||
<el-table-column prop="type" label="消息类型" width="120">
|
||||
<template v-slot="scope">
|
||||
<span v-if="'system'===scope.row.type">系统消息</span>
|
||||
<span v-if="'user'===scope.row.type">用户消息</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="createdAt" label="接收时间">
|
||||
<template slot-scope="scope">{{$moment(scope.row.createdAt).format("YYYY-MM-DD HH:mm:ss")}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="status" label="状态">
|
||||
<template v-slot="scope">
|
||||
<el-tag size="mini" v-if="0 === scope.row.status">未阅读</el-tag>
|
||||
<el-tag size="mini" type="warning" v-else-if="1 === scope.row.status && scope.row.needBack">已阅读未反馈</el-tag>
|
||||
<el-tag size="mini" type="success" v-else-if="1 === scope.row.status && !scope.row.needBack">已阅读无需反馈</el-tag>
|
||||
<el-tag size="mini" v-else-if="2 === scope.row.status">已阅读已反馈</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="backFiles" label="反馈附件">
|
||||
<template v-slot="scope">
|
||||
<el-link type="primary" v-if="scope.row.backFiles.length > 0" @click="viewFiles(scope.row)">点击查看</el-link>
|
||||
<el-link type="info" v-else>暂无</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="primary" @click="openView(scope.row)">查看</el-button>
|
||||
<el-button v-if="scope.row.needBack" size="mini" type="primary" @click="openBack(scope.row)">反馈</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
<template #view>
|
||||
<div style="text-align: center">
|
||||
<h4>{{viewData.title}}</h4>
|
||||
</div>
|
||||
<div style="text-align: center; font-size: 14px; line-height: 24px">{{$moment(viewData.createdAt).format('YYYY-MM-DD HH:mm')}}</div>
|
||||
<div style="margin-top: 16px !important; border-bottom: 1px solid #ddd"></div>
|
||||
<div style="margin-top: 16px !important;" v-html="viewData.note"></div>
|
||||
<file-preview complete_result :files="viewData.files"></file-preview>
|
||||
</template>
|
||||
</guava>
|
||||
<el-dialog :visible.sync="backDialogVisible" title="反馈附件" width="40%" top="2%">
|
||||
<el-form v-model="backFormData" ref="backFormRef" label-width="80px">
|
||||
<el-form-item label="反馈附件" prop="files">
|
||||
<file-upload
|
||||
v-if="isViewFiles === false"
|
||||
:value.sync="backFormData.files"
|
||||
upload_mode="drag"
|
||||
:upload_number="5"
|
||||
upload_result_category="array"
|
||||
complete_result
|
||||
upload_mode="file"
|
||||
></file-upload>
|
||||
<div v-else style="width: 100%; text-align: center">
|
||||
<file-preview :files="backFormData.files" complete_result></file-preview>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="反馈内容" prop="backText">
|
||||
<el-input v-model="backFormData.backText" type="textarea" :rows="4" placeholder="非必填" max="500" :readonly="isViewFiles"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer" v-if="isViewFiles === false">
|
||||
<el-button @click="backDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="backDo">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data: function () {
|
||||
return {
|
||||
backFormData: {},
|
||||
backFiles: [],
|
||||
backDialogVisible: false,
|
||||
viewData: {},
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
searchType: "all",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
status: "unread"
|
||||
},
|
||||
isViewFiles: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
viewFiles(row) {
|
||||
this.isViewFiles = true
|
||||
this.backFormData = {
|
||||
files: row.backFiles,
|
||||
backText: row.backText
|
||||
}
|
||||
this.backDialogVisible = true
|
||||
},
|
||||
async backDo() {
|
||||
if (this.backFormData.files.length === 0) {
|
||||
this.$message.warning("请上传反馈附件")
|
||||
return
|
||||
}
|
||||
|
||||
const resp = await this.$axios.post("/platform/sys/msg/user/backDo", {
|
||||
id: this.backFormData.id,
|
||||
backText: this.backFormData.backText,
|
||||
files: JSON.stringify(this.backFormData.files)
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
this.backDialogVisible = false
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
async openView(row) {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/user/findOne", { id: row.msgId })
|
||||
this.viewData = resp.data
|
||||
this.$refs.guava.view()
|
||||
this.pageData()
|
||||
window.webSocketPubSub.send(
|
||||
JSON.stringify({
|
||||
room: store.state.room,
|
||||
action: "getMessage"
|
||||
})
|
||||
)
|
||||
},
|
||||
openBack(row) {
|
||||
this.isViewFiles = false
|
||||
this.backFormData = {
|
||||
files: row.backFiles,
|
||||
backText: row.backText,
|
||||
id: row.id
|
||||
}
|
||||
this.backDialogVisible = true
|
||||
},
|
||||
async pageData() {
|
||||
const resp = await this.$axios.post("/platform/sys/msg/user/data/" + this.pageForm.status, this.pageForm)
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.tableData.forEach((item) => {
|
||||
item.backFiles = item.backFiles ? JSON.parse(item.backFiles) : []
|
||||
})
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
formatAt: function (val) {
|
||||
if (val && val > 0) return this.$moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageForm.searchType = GetQueryString("type") ? GetQueryString("type") : "all"
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,209 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.el-msg-active {
|
||||
background-color: #546478;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<a
|
||||
href="${base}/platform/sys/msg/user/read"
|
||||
data-pjax
|
||||
class="el-button el-button--default el-button--medium"
|
||||
:class="{'el-msg-active':'all'==pageForm.searchType}"
|
||||
>
|
||||
全部类型消息
|
||||
</a>
|
||||
<a
|
||||
href="${base}/platform/sys/msg/user/read?type=system"
|
||||
data-pjax
|
||||
class="el-button el-button--default el-button--medium"
|
||||
:class="{'el-msg-active':'system'==pageForm.searchType}"
|
||||
>
|
||||
系统消息
|
||||
<span v-if="unreadnum.system>0">({{unreadnum.system}})</span>
|
||||
</a>
|
||||
<a
|
||||
href="${base}/platform/sys/msg/user/read?type=user"
|
||||
data-pjax
|
||||
class="el-button el-button--default el-button--medium"
|
||||
:class="{'el-msg-active':'user'==pageForm.searchType}"
|
||||
>
|
||||
用户消息
|
||||
<span v-if="unreadnum.user>0">({{unreadnum.user}})</span>
|
||||
</a>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
@sort-change="pageOrder"
|
||||
size="small"
|
||||
header-align="center"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="35"></el-table-column>
|
||||
<el-table-column prop="status" width="45">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="0==scope.row.status"><i class="fa fa-circle text-info ml5"></i></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="消息类型" header-align="center" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="'system'==scope.row.type">系统消息</span>
|
||||
<span v-if="'user'==scope.row.type">用户消息</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" header-align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">
|
||||
<a :href="'${base}/platform/sys/msg/user/read/detail/'+scope.row.msgid" data-pjax>{{scope.row.title}}</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="opat" label="接收时间" header-align="center" align="center">
|
||||
<template slot-scope="scope">{{formatAt(scope.row.opat)}}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container">
|
||||
<el-pagination
|
||||
style="float: left"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
selectDialogVisible: false,
|
||||
viewDialogVisible: false,
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
searchType: "${type!}",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "createdAt",
|
||||
pageOrderBy: "descending"
|
||||
},
|
||||
formData: {
|
||||
id: "",
|
||||
type: "system"
|
||||
},
|
||||
selectData: [],
|
||||
unreadnum: {
|
||||
system: 0,
|
||||
user: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/data/read",
|
||||
self.pageForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list
|
||||
self.pageForm.totalCount = data.data.totalCount
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
formatAt: function (val) {
|
||||
if (val && val > 0) return moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
},
|
||||
handleSelectionChange: function (val) {
|
||||
this.selectData = val
|
||||
},
|
||||
delMore: function () {
|
||||
var self = this
|
||||
if (self.selectData.length == 0) {
|
||||
self.$message({
|
||||
message: "请选择消息",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
var ids = []
|
||||
self.selectData.forEach(function (val) {
|
||||
ids.push(val.id)
|
||||
})
|
||||
self.$confirm("您确定要删除选中的 " + ids.length + " 条消息? ", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/delete",
|
||||
{ ids: ids.toString() },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.pageData()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,302 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.el-msg-active {
|
||||
background-color: #546478;
|
||||
color: #ffffff;
|
||||
}
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<a
|
||||
href="${base}/platform/sys/msg/user/unread"
|
||||
data-pjax
|
||||
class="el-button el-button--default el-button--medium"
|
||||
:class="{'el-msg-active':'all'==pageForm.searchType}"
|
||||
>
|
||||
全部类型消息
|
||||
</a>
|
||||
<a
|
||||
href="${base}/platform/sys/msg/user/unread?type=system"
|
||||
data-pjax
|
||||
class="el-button el-button--default el-button--medium"
|
||||
:class="{'el-msg-active':'system'==pageForm.searchType}"
|
||||
>
|
||||
系统消息
|
||||
<span v-if="unreadnum.system>0">({{unreadnum.system}})</span>
|
||||
</a>
|
||||
<a
|
||||
href="${base}/platform/sys/msg/user/unread?type=user"
|
||||
data-pjax
|
||||
class="el-button el-button--default el-button--medium"
|
||||
:class="{'el-msg-active':'user'==pageForm.searchType}"
|
||||
>
|
||||
用户消息
|
||||
<span v-if="unreadnum.user>0">({{unreadnum.user}})</span>
|
||||
</a>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
@sort-change="pageOrder"
|
||||
size="small"
|
||||
header-align="center"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="35"></el-table-column>
|
||||
<el-table-column prop="status" width="45">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="0==scope.row.status"><i class="fa fa-circle text-info ml5"></i></span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="type" label="消息类型" header-align="center" align="center" width="120">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="'system'==scope.row.type">系统消息</span>
|
||||
<span v-if="'user'==scope.row.type">用户消息</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="title" label="标题" header-align="center" :show-overflow-tooltip="true">
|
||||
<template slot-scope="scope">
|
||||
<a :href="'${base}/platform/sys/msg/user/unread/detail/'+scope.row.msgid" data-pjax>{{scope.row.title}}</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="opat" label="接收时间" header-align="center" align="center">
|
||||
<template slot-scope="scope">{{formatAt(scope.row.opat)}}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container">
|
||||
<el-pagination
|
||||
style="float: left"
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
selectDialogVisible: false,
|
||||
viewDialogVisible: false,
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
searchType: "${type!}",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "createdAt",
|
||||
pageOrderBy: "descending"
|
||||
},
|
||||
formData: {
|
||||
id: "",
|
||||
type: "system"
|
||||
},
|
||||
selectData: [],
|
||||
unreadnum: {
|
||||
system: 0,
|
||||
user: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/data/unread",
|
||||
self.pageForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list
|
||||
self.pageForm.totalCount = data.data.totalCount
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
formatAt: function (val) {
|
||||
if (val && val > 0) return moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
},
|
||||
handleSelectionChange: function (val) {
|
||||
this.selectData = val
|
||||
},
|
||||
unreadData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/unread_num",
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.unreadnum = data.data
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
readMore: function () {
|
||||
var self = this
|
||||
if (self.selectData.length == 0) {
|
||||
self.$message({
|
||||
message: "请选择消息",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
var ids = []
|
||||
self.selectData.forEach(function (val) {
|
||||
ids.push(val.id)
|
||||
})
|
||||
self.$confirm("您确定要标记选中的 " + ids.length + " 条消息为已读吗? ", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/status/read",
|
||||
{ ids: ids.toString() },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.pageData()
|
||||
self.unreadData()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
readAll: function () {
|
||||
var self = this
|
||||
self.$confirm("您确定要标记全部消息为已读吗? ", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/status/readAll",
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.pageData()
|
||||
self.unreadData()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
delMore: function () {
|
||||
var self = this
|
||||
if (self.selectData.length == 0) {
|
||||
self.$message({
|
||||
message: "请选择消息",
|
||||
type: "warning"
|
||||
})
|
||||
return false
|
||||
}
|
||||
var ids = []
|
||||
self.selectData.forEach(function (val) {
|
||||
ids.push(val.id)
|
||||
})
|
||||
self.$confirm("您确定要删除选中的 " + ids.length + " 条消息? ", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/msg/user/delete",
|
||||
{ ids: ids.toString() },
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.pageData()
|
||||
self.unreadData()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
this.unreadData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-alert title="对不起,您没有权限访问该页面。" type="error"></el-alert>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app"
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,149 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<el-row type="flex">
|
||||
<el-input v-model="pageForm.templateCode" maxlength="50" style="width: 400px" clearable placeholder="请输入模板代码"></el-input>
|
||||
<el-button @click="doSearch" icon="el-icon-search" type="primary" class="ml10">搜索</el-button>
|
||||
<el-button type="primary" @click="openAdd" style="margin-left: auto">新增模板</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table :data="tableData" border class="mt10" stripe ref="table" :height="tableHeight" id="officeTable">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="70px"></el-table-column>
|
||||
<el-table-column label="模板代码" prop="templateCode"></el-table-column>
|
||||
<el-table-column label="模板名称" prop="templateName"></el-table-column>
|
||||
<el-table-column label="模板路径" prop="templatePath" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="模板说明" prop="templateDesc"></el-table-column>
|
||||
<el-table-column label="操作" width="300px">
|
||||
<template scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="download(row.templatePath)">下载</el-button>
|
||||
<el-button size="mini" type="primary" @click="preview(row)">预览</el-button>
|
||||
<el-button size="mini" type="primary" @click="openEdit(row)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="模板" :visible.sync="dialogVisible" :close-on-click-modal="false" width="800px">
|
||||
<el-form :model="formData" ref="ruleForm" :rules="formRules" label-width="80px">
|
||||
<el-form-item label="模板文件" prop="file" :rules="[{required:true,message:'必传',trigger:['change']}]">
|
||||
<file-upload :value.sync="formData.file" :upload_number="1" upload_result_category="array" complete_result></file-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="模板名称" prop="templateName" :rules="[{required:true,message:'必填',trigger:['change']}]">
|
||||
<el-input type="text" v-model="formData.templateName" maxlength="50" placeholder="必填"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="模板代码" prop="templateCode" :rules="[{required:true,message:'必填',trigger:['change']}]">
|
||||
<el-input type="text" v-model="formData.templateCode" maxlength="50" placeholder="请填写模板代码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="模板说明" prop="templateDesc" :rules="[{required:true,message:'必填',trigger:['change']}]">
|
||||
<el-input type="text" v-model="formData.templateDesc" maxlength="50" placeholder="请填写模板说明"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="save">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
tableHeight: 700,
|
||||
formData: {
|
||||
templateName: null
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"formData.file": {
|
||||
handler: function (val) {
|
||||
if (val) {
|
||||
this.$set(this.formData, "templateName", val[0].name)
|
||||
this.$set(this.formData, "templatePath", val[0].response.data)
|
||||
} else {
|
||||
this.$set(this.formData, "templateName", null)
|
||||
this.$set(this.formData, "templatePath", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openAdd() {
|
||||
this.dialogVisible = true
|
||||
this.formData = {}
|
||||
},
|
||||
openEdit(row) {
|
||||
this.dialogVisible = true
|
||||
this.formData = { ...row }
|
||||
},
|
||||
save() {
|
||||
this.$refs.ruleForm.validate(async (valid) => {
|
||||
if (valid) {
|
||||
const formData = this.formData
|
||||
// delete formData.file
|
||||
this.$axios
|
||||
.post(loc() + "/save", {
|
||||
officeTemplate: JSON.stringify(this.formData)
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.dialogVisible = false
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post(loc() + "/delete", { id })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
download(path) {
|
||||
window.open(path)
|
||||
},
|
||||
preview(row) {
|
||||
this.$commonUtil.previewFile({
|
||||
suffix: row.templateName.split(".").pop().toLocaleLowerCase(),
|
||||
downloadPath: row.templatePath,
|
||||
name: row.templateName,
|
||||
id: row.templatePath.split("=").pop().toLocaleLowerCase()
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
setTimeout(() => {
|
||||
this.tableHeight = window.innerHeight - document.querySelector("#officeTable").getBoundingClientRect().top - 80
|
||||
}, 200)
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,65 @@
|
||||
const sort_component = {
|
||||
template: `
|
||||
<el-dialog title="排序" :visible.sync="sortDialogVisible" width="50%">
|
||||
<el-tree ref="sortTreeRef" :data="sortData" draggable :allow-drop="sortAllowDrop" node-key="id"
|
||||
style="height: 60vh;overflow-y: auto"
|
||||
:props="defaultProps">
|
||||
<span class="custom-tree-node" slot-scope="scope">
|
||||
<span>{{ scope.node.label }}</span>
|
||||
</span>
|
||||
</el-tree>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="sortDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSort">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
sortDialogVisible: false,
|
||||
sortData: [],
|
||||
defaultProps: {
|
||||
children: "children",
|
||||
label: "name"
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen() {
|
||||
this.sortDialogVisible = true
|
||||
this.$axios.post("/platform/sys/role/sortData").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.sortData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
sortAllowDrop(moveNode, inNode, type) {
|
||||
return type !== "inner"
|
||||
},
|
||||
doSort() {
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "正在提交...",
|
||||
spinner: "el-icon-loading"
|
||||
})
|
||||
const afterSorts = this.sortData.map((v) => {
|
||||
return {
|
||||
id: v.id,
|
||||
name: v.name
|
||||
}
|
||||
})
|
||||
this.$axios
|
||||
.post("/platform/sys/role/doSort", { roles: JSON.stringify(afterSorts) })
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.sortDialogVisible = false
|
||||
this.$emit("refresh", null)
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="ti-plus"></i>
|
||||
新建路由
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%">
|
||||
<el-table-column header-align="center" label="原路径" prop="url" sortable></el-table-column>
|
||||
<el-table-column :show-overflow-tooltip="true" header-align="center" label="指向路径" prop="toUrl"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="转发方式" prop="type">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.type=='hide'">隐性转发</span>
|
||||
<span v-if="scope.row.type=='show'">显性转发</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="启用状态" prop="disabled" sortable>
|
||||
<template slot-scope="scope">
|
||||
<i class="fa fa-circle text-danger ml5" v-if="scope.row.disabled"></i>
|
||||
<i class="fa fa-circle text-success ml5" v-if="!scope.row.disabled"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'enable',id:scope.row.id,name:scope.row.name,row:scope.row}">启用</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',id:scope.row.id,name:scope.row.name,row:scope.row}">
|
||||
禁用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'edit',id:scope.row.id}" divided>修改</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',id:scope.row.id,url:scope.row.url}">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog title="新建路由" :visible.sync="addDialogVisible" width="40%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="url" label="原路径">
|
||||
<el-input maxlength="100" placeholder="原路径" v-model="formData.url" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="toUrl" label="指向路径">
|
||||
<el-input placeholder="指向路径" v-model="formData.toUrl" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="指向路径">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-radio label="hide">隐性转发</el-radio>
|
||||
<el-radio label="show">显性转发</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="修改路由" :visible.sync="editDialogVisible" width="40%">
|
||||
<el-form :model="formData" ref="editForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="url" label="原路径">
|
||||
<el-input maxlength="100" placeholder="原路径" v-model="formData.url" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="toUrl" label="指向路径">
|
||||
<el-input placeholder="指向路径" v-model="formData.toUrl" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="type" label="指向路径">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-radio label="hide">隐性转发</el-radio>
|
||||
<el-radio label="show">显性转发</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
formData: {
|
||||
id: "",
|
||||
type: "hide"
|
||||
},
|
||||
formRules: {
|
||||
url: [{ required: true, message: "原路径", trigger: "blur" }],
|
||||
toUrl: [{ required: true, message: "指向路径", trigger: "blur" }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/route/data",
|
||||
self.pageForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list
|
||||
self.pageForm.totalCount = data.data.totalCount
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
formatAt: function (val) {
|
||||
if (val && val > 0) return moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
},
|
||||
doSearch: function () {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
openAdd: function () {
|
||||
var self = this
|
||||
this.addDialogVisible = true
|
||||
this.formData = {
|
||||
id: "",
|
||||
type: "hide"
|
||||
} //打开新增窗口,表单先清空
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
doAdd: function () {
|
||||
var self = this
|
||||
self.$refs["addForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
base + "/platform/sys/route/addDo",
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.addDialogVisible = false
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
self.pageData() //添加失败也可以入库,所以加载一下看看
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
doEdit: function () {
|
||||
var self = this
|
||||
self.$refs["editForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
base + "/platform/sys/route/editDo",
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.doSearch() //查询是带条件的,加载新建用户选择的单位用户数据
|
||||
//self.pageData();
|
||||
self.editDialogVisible = false
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
dropdownCommand: function (command) {
|
||||
//监听下拉框事件
|
||||
var self = this
|
||||
if ("enable" == command.type || "disable" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/route/" + command.type + "/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
if ("disable" == command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" == command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("edit" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/route/edit/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.formData = data.data //加载后台表单数据
|
||||
self.editDialogVisible = true //打开编辑窗口
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("delete" == command.type) {
|
||||
self.$confirm("此操作将删除 " + command.url, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/route/delete/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.doSearch()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,84 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>签字</title>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover" />
|
||||
|
||||
<script src="${base!}/assets/platform/plugins/vue/vue.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/vue-http-loader/httpVueLoader.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/smooth-signature/index.umd.min.js"></script>
|
||||
<script src="${base!}/assets/platform/plugins/axios/axios.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<signature @save="save"></signature>
|
||||
</div>
|
||||
</body>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
components: {
|
||||
signature: httpVueLoader("/components/plugins/sysSignature/index.vue?v=" + new Date().getTime())
|
||||
},
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
save(signature) {
|
||||
const id = new URLSearchParams(window.location.search).get("id")
|
||||
const origin = new URLSearchParams(window.location.search).get("origin")
|
||||
if (!id && !origin) {
|
||||
alert("参数错误")
|
||||
return
|
||||
}
|
||||
|
||||
const file = this.base64ToFile(signature, "signature.png")
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
formData.append("id", id)
|
||||
formData.append("origin", origin)
|
||||
|
||||
const method = origin === "user" ? "/update" : "/saveSignature"
|
||||
|
||||
axios
|
||||
.post("/platform/signature" + method, formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
return res.data
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
alert("保存成功")
|
||||
} else {
|
||||
alert("保存失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
base64ToFile(base64Data, filename) {
|
||||
// 将base64的数据部分提取出来
|
||||
const parts = base64Data.split(";base64,")
|
||||
const contentType = parts[0].split(":")[1]
|
||||
const raw = window.atob(parts[1])
|
||||
const rawLength = raw.length
|
||||
const uInt8Array = new Uint8Array(rawLength)
|
||||
|
||||
for (let i = 0; i < rawLength; ++i) {
|
||||
uInt8Array[i] = raw.charCodeAt(i)
|
||||
}
|
||||
|
||||
// 使用Blob对象创建File对象
|
||||
const blob = new Blob([uInt8Array], { type: contentType })
|
||||
blob.lastModifiedDate = new Date()
|
||||
blob.name = filename
|
||||
return new File([blob], filename, { type: contentType })
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</html>
|
||||
@@ -0,0 +1,732 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Cron表达式生成器</title>
|
||||
<link href="${base}/assets/platform/cron/css/easyui.min.css" rel="stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
.line {
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.imp {
|
||||
padding-left: 25px;
|
||||
}
|
||||
|
||||
.col {
|
||||
width: 95px;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
li {
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
body {
|
||||
text-align: center; /*IE*/
|
||||
text-align: -moz-center; /*Firefox*/
|
||||
text-align: -webkit-center; /*Chrome*/
|
||||
}
|
||||
</style>
|
||||
<script nonce="${cspNonce!}">
|
||||
var base = "${base!}"
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="easyui-layout" style="width: 830px; height: 560px; border: 1px rgb(202, 196, 196) solid; border-radius: 5px">
|
||||
<div style="height: 100%">
|
||||
<div class="easyui-tabs" data-options="fit:true,border:false">
|
||||
<div title="秒">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="second" onclick="everyTime(this)" />
|
||||
每秒 允许的通配符[, - * /]
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="second" onclick="cycle(this)" />
|
||||
周期从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:58" value="1" id="secondStart_0" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:2,max:59" value="2" id="secondEnd_0" />
|
||||
秒
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="second" onclick="startOn(this)" />
|
||||
从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:0,max:59" value="0" id="secondStart_1" />
|
||||
秒开始,每
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:59" value="1" id="secondEnd_1" />
|
||||
秒执行一次
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="second" id="sencond_appoint" />
|
||||
指定
|
||||
</div>
|
||||
<div class="imp secondList">
|
||||
<input type="checkbox" value="0" />
|
||||
00
|
||||
<input type="checkbox" value="1" />
|
||||
01
|
||||
<input type="checkbox" value="2" />
|
||||
02
|
||||
<input type="checkbox" value="3" />
|
||||
03
|
||||
<input type="checkbox" value="4" />
|
||||
04
|
||||
<input type="checkbox" value="5" />
|
||||
05
|
||||
<input type="checkbox" value="6" />
|
||||
06
|
||||
<input type="checkbox" value="7" />
|
||||
07
|
||||
<input type="checkbox" value="8" />
|
||||
08
|
||||
<input type="checkbox" value="9" />
|
||||
09
|
||||
</div>
|
||||
<div class="imp secondList">
|
||||
<input type="checkbox" value="10" />
|
||||
10
|
||||
<input type="checkbox" value="11" />
|
||||
11
|
||||
<input type="checkbox" value="12" />
|
||||
12
|
||||
<input type="checkbox" value="13" />
|
||||
13
|
||||
<input type="checkbox" value="14" />
|
||||
14
|
||||
<input type="checkbox" value="15" />
|
||||
15
|
||||
<input type="checkbox" value="16" />
|
||||
16
|
||||
<input type="checkbox" value="17" />
|
||||
17
|
||||
<input type="checkbox" value="18" />
|
||||
18
|
||||
<input type="checkbox" value="19" />
|
||||
19
|
||||
</div>
|
||||
<div class="imp secondList">
|
||||
<input type="checkbox" value="20" />
|
||||
20
|
||||
<input type="checkbox" value="21" />
|
||||
21
|
||||
<input type="checkbox" value="22" />
|
||||
22
|
||||
<input type="checkbox" value="23" />
|
||||
23
|
||||
<input type="checkbox" value="24" />
|
||||
24
|
||||
<input type="checkbox" value="25" />
|
||||
25
|
||||
<input type="checkbox" value="26" />
|
||||
26
|
||||
<input type="checkbox" value="27" />
|
||||
27
|
||||
<input type="checkbox" value="28" />
|
||||
28
|
||||
<input type="checkbox" value="29" />
|
||||
29
|
||||
</div>
|
||||
<div class="imp secondList">
|
||||
<input type="checkbox" value="30" />
|
||||
30
|
||||
<input type="checkbox" value="31" />
|
||||
31
|
||||
<input type="checkbox" value="32" />
|
||||
32
|
||||
<input type="checkbox" value="33" />
|
||||
33
|
||||
<input type="checkbox" value="34" />
|
||||
34
|
||||
<input type="checkbox" value="35" />
|
||||
35
|
||||
<input type="checkbox" value="36" />
|
||||
36
|
||||
<input type="checkbox" value="37" />
|
||||
37
|
||||
<input type="checkbox" value="38" />
|
||||
38
|
||||
<input type="checkbox" value="39" />
|
||||
39
|
||||
</div>
|
||||
<div class="imp secondList">
|
||||
<input type="checkbox" value="40" />
|
||||
40
|
||||
<input type="checkbox" value="41" />
|
||||
41
|
||||
<input type="checkbox" value="42" />
|
||||
42
|
||||
<input type="checkbox" value="43" />
|
||||
43
|
||||
<input type="checkbox" value="44" />
|
||||
44
|
||||
<input type="checkbox" value="45" />
|
||||
45
|
||||
<input type="checkbox" value="46" />
|
||||
46
|
||||
<input type="checkbox" value="47" />
|
||||
47
|
||||
<input type="checkbox" value="48" />
|
||||
48
|
||||
<input type="checkbox" value="49" />
|
||||
49
|
||||
</div>
|
||||
<div class="imp secondList">
|
||||
<input type="checkbox" value="50" />
|
||||
50
|
||||
<input type="checkbox" value="51" />
|
||||
51
|
||||
<input type="checkbox" value="52" />
|
||||
52
|
||||
<input type="checkbox" value="53" />
|
||||
53
|
||||
<input type="checkbox" value="54" />
|
||||
54
|
||||
<input type="checkbox" value="55" />
|
||||
55
|
||||
<input type="checkbox" value="56" />
|
||||
56
|
||||
<input type="checkbox" value="57" />
|
||||
57
|
||||
<input type="checkbox" value="58" />
|
||||
58
|
||||
<input type="checkbox" value="59" />
|
||||
59
|
||||
</div>
|
||||
</div>
|
||||
<div title="分钟">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="min" onclick="everyTime(this)" />
|
||||
分钟 允许的通配符[, - * /]
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="min" onclick="cycle(this)" />
|
||||
周期从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:58" value="1" id="minStart_0" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:2,max:59" value="2" id="minEnd_0" />
|
||||
分钟
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="min" onclick="startOn(this)" />
|
||||
从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:0,max:59" value="0" id="minStart_1" />
|
||||
分钟开始,每
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:59" value="1" id="minEnd_1" />
|
||||
分钟执行一次
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="min" id="min_appoint" />
|
||||
指定
|
||||
</div>
|
||||
<div class="imp minList">
|
||||
<input type="checkbox" value="0" />
|
||||
00
|
||||
<input type="checkbox" value="1" />
|
||||
01
|
||||
<input type="checkbox" value="2" />
|
||||
02
|
||||
<input type="checkbox" value="3" />
|
||||
03
|
||||
<input type="checkbox" value="4" />
|
||||
04
|
||||
<input type="checkbox" value="5" />
|
||||
05
|
||||
<input type="checkbox" value="6" />
|
||||
06
|
||||
<input type="checkbox" value="7" />
|
||||
07
|
||||
<input type="checkbox" value="8" />
|
||||
08
|
||||
<input type="checkbox" value="9" />
|
||||
09
|
||||
</div>
|
||||
<div class="imp minList">
|
||||
<input type="checkbox" value="10" />
|
||||
10
|
||||
<input type="checkbox" value="11" />
|
||||
11
|
||||
<input type="checkbox" value="12" />
|
||||
12
|
||||
<input type="checkbox" value="13" />
|
||||
13
|
||||
<input type="checkbox" value="14" />
|
||||
14
|
||||
<input type="checkbox" value="15" />
|
||||
15
|
||||
<input type="checkbox" value="16" />
|
||||
16
|
||||
<input type="checkbox" value="17" />
|
||||
17
|
||||
<input type="checkbox" value="18" />
|
||||
18
|
||||
<input type="checkbox" value="19" />
|
||||
19
|
||||
</div>
|
||||
<div class="imp minList">
|
||||
<input type="checkbox" value="20" />
|
||||
20
|
||||
<input type="checkbox" value="21" />
|
||||
21
|
||||
<input type="checkbox" value="22" />
|
||||
22
|
||||
<input type="checkbox" value="23" />
|
||||
23
|
||||
<input type="checkbox" value="24" />
|
||||
24
|
||||
<input type="checkbox" value="25" />
|
||||
25
|
||||
<input type="checkbox" value="26" />
|
||||
26
|
||||
<input type="checkbox" value="27" />
|
||||
27
|
||||
<input type="checkbox" value="28" />
|
||||
28
|
||||
<input type="checkbox" value="29" />
|
||||
29
|
||||
</div>
|
||||
<div class="imp minList">
|
||||
<input type="checkbox" value="30" />
|
||||
30
|
||||
<input type="checkbox" value="31" />
|
||||
31
|
||||
<input type="checkbox" value="32" />
|
||||
32
|
||||
<input type="checkbox" value="33" />
|
||||
33
|
||||
<input type="checkbox" value="34" />
|
||||
34
|
||||
<input type="checkbox" value="35" />
|
||||
35
|
||||
<input type="checkbox" value="36" />
|
||||
36
|
||||
<input type="checkbox" value="37" />
|
||||
37
|
||||
<input type="checkbox" value="38" />
|
||||
38
|
||||
<input type="checkbox" value="39" />
|
||||
39
|
||||
</div>
|
||||
<div class="imp minList">
|
||||
<input type="checkbox" value="40" />
|
||||
40
|
||||
<input type="checkbox" value="41" />
|
||||
41
|
||||
<input type="checkbox" value="42" />
|
||||
42
|
||||
<input type="checkbox" value="43" />
|
||||
43
|
||||
<input type="checkbox" value="44" />
|
||||
44
|
||||
<input type="checkbox" value="45" />
|
||||
45
|
||||
<input type="checkbox" value="46" />
|
||||
46
|
||||
<input type="checkbox" value="47" />
|
||||
47
|
||||
<input type="checkbox" value="48" />
|
||||
48
|
||||
<input type="checkbox" value="49" />
|
||||
49
|
||||
</div>
|
||||
<div class="imp minList">
|
||||
<input type="checkbox" value="50" />
|
||||
50
|
||||
<input type="checkbox" value="51" />
|
||||
51
|
||||
<input type="checkbox" value="52" />
|
||||
52
|
||||
<input type="checkbox" value="53" />
|
||||
53
|
||||
<input type="checkbox" value="54" />
|
||||
54
|
||||
<input type="checkbox" value="55" />
|
||||
55
|
||||
<input type="checkbox" value="56" />
|
||||
56
|
||||
<input type="checkbox" value="57" />
|
||||
57
|
||||
<input type="checkbox" value="58" />
|
||||
58
|
||||
<input type="checkbox" value="59" />
|
||||
59
|
||||
</div>
|
||||
</div>
|
||||
<div title="小时">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="hour" onclick="everyTime(this)" />
|
||||
小时 允许的通配符[, - * /]
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="hour" onclick="cycle(this)" />
|
||||
周期从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:0,max:23" value="0" id="hourStart_0" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:2,max:23" value="2" id="hourEnd_1" />
|
||||
小时
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="hour" onclick="startOn(this)" />
|
||||
从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:0,max:23" value="0" id="hourStart_1" />
|
||||
小时开始,每
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:23" value="1" id="hourEnd_1" />
|
||||
小时执行一次
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="hour" id="hour_appoint" />
|
||||
指定
|
||||
</div>
|
||||
<div class="imp hourList">
|
||||
AM:
|
||||
<input type="checkbox" value="0" />
|
||||
00
|
||||
<input type="checkbox" value="1" />
|
||||
01
|
||||
<input type="checkbox" value="2" />
|
||||
02
|
||||
<input type="checkbox" value="3" />
|
||||
03
|
||||
<input type="checkbox" value="4" />
|
||||
04
|
||||
<input type="checkbox" value="5" />
|
||||
05
|
||||
<input type="checkbox" value="6" />
|
||||
06
|
||||
<input type="checkbox" value="7" />
|
||||
07
|
||||
<input type="checkbox" value="8" />
|
||||
08
|
||||
<input type="checkbox" value="9" />
|
||||
09
|
||||
<input type="checkbox" value="10" />
|
||||
10
|
||||
<input type="checkbox" value="11" />
|
||||
11
|
||||
</div>
|
||||
<div class="imp hourList">
|
||||
PM:
|
||||
<input type="checkbox" value="12" />
|
||||
12
|
||||
<input type="checkbox" value="13" />
|
||||
13
|
||||
<input type="checkbox" value="14" />
|
||||
14
|
||||
<input type="checkbox" value="15" />
|
||||
15
|
||||
<input type="checkbox" value="16" />
|
||||
16
|
||||
<input type="checkbox" value="17" />
|
||||
17
|
||||
<input type="checkbox" value="18" />
|
||||
18
|
||||
<input type="checkbox" value="19" />
|
||||
19
|
||||
<input type="checkbox" value="20" />
|
||||
20
|
||||
<input type="checkbox" value="21" />
|
||||
21
|
||||
<input type="checkbox" value="22" />
|
||||
22
|
||||
<input type="checkbox" value="23" />
|
||||
23
|
||||
</div>
|
||||
</div>
|
||||
<div title="日">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="day" onclick="everyTime(this)" />
|
||||
日 允许的通配符[, - * / L W]
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="day" onclick="unAppoint(this)" />
|
||||
不指定
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="day" onclick="cycle(this)" />
|
||||
周期从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:31" value="1" id="dayStart_0" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:2,max:31" value="2" id="dayEnd_0" />
|
||||
日
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="day" onclick="startOn(this)" />
|
||||
从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:31" value="1" id="dayStart_1" />
|
||||
日开始,每
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:31" value="1" id="dayEnd_1" />
|
||||
天执行一次
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="day" onclick="workDay(this)" />
|
||||
每月
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:31" value="1" id="dayStart_2" />
|
||||
号最近的那个工作日
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="day" onclick="lastDay(this)" />
|
||||
本月最后一天
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="day" id="day_appoint" />
|
||||
指定
|
||||
</div>
|
||||
<div class="imp dayList">
|
||||
<input type="checkbox" value="1" />
|
||||
1
|
||||
<input type="checkbox" value="2" />
|
||||
2
|
||||
<input type="checkbox" value="3" />
|
||||
3
|
||||
<input type="checkbox" value="4" />
|
||||
4
|
||||
<input type="checkbox" value="5" />
|
||||
5
|
||||
<input type="checkbox" value="6" />
|
||||
6
|
||||
<input type="checkbox" value="7" />
|
||||
7
|
||||
<input type="checkbox" value="8" />
|
||||
8
|
||||
<input type="checkbox" value="9" />
|
||||
9
|
||||
<input type="checkbox" value="10" />
|
||||
10
|
||||
<input type="checkbox" value="11" />
|
||||
11
|
||||
<input type="checkbox" value="12" />
|
||||
12
|
||||
<input type="checkbox" value="13" />
|
||||
13
|
||||
<input type="checkbox" value="14" />
|
||||
14
|
||||
<input type="checkbox" value="15" />
|
||||
15
|
||||
<input type="checkbox" value="16" />
|
||||
16
|
||||
</div>
|
||||
<div class="imp dayList">
|
||||
<input type="checkbox" value="17" />
|
||||
17
|
||||
<input type="checkbox" value="18" />
|
||||
18
|
||||
<input type="checkbox" value="19" />
|
||||
19
|
||||
<input type="checkbox" value="20" />
|
||||
20
|
||||
<input type="checkbox" value="21" />
|
||||
21
|
||||
<input type="checkbox" value="22" />
|
||||
22
|
||||
<input type="checkbox" value="23" />
|
||||
23
|
||||
<input type="checkbox" value="24" />
|
||||
24
|
||||
<input type="checkbox" value="25" />
|
||||
25
|
||||
<input type="checkbox" value="26" />
|
||||
26
|
||||
<input type="checkbox" value="27" />
|
||||
27
|
||||
<input type="checkbox" value="28" />
|
||||
28
|
||||
<input type="checkbox" value="29" />
|
||||
29
|
||||
<input type="checkbox" value="30" />
|
||||
30
|
||||
<input type="checkbox" value="31" />
|
||||
31
|
||||
</div>
|
||||
</div>
|
||||
<div title="月">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="mouth" onclick="everyTime(this)" />
|
||||
月 允许的通配符[, - * /]
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="mouth" onclick="unAppoint(this)" />
|
||||
不指定
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="mouth" onclick="cycle(this)" />
|
||||
周期从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:12" value="1" id="mouthStart_0" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:2,max:12" value="2" id="mouthEnd_0" />
|
||||
月
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="mouth" onclick="startOn(this)" />
|
||||
从
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:12" value="1" id="mouthStart_1" />
|
||||
日开始,每
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:12" value="1" id="mouthEnd_1" />
|
||||
月执行一次
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="mouth" id="mouth_appoint" />
|
||||
指定
|
||||
</div>
|
||||
<div class="imp mouthList">
|
||||
<input type="checkbox" value="1" />
|
||||
1
|
||||
<input type="checkbox" value="2" />
|
||||
2
|
||||
<input type="checkbox" value="3" />
|
||||
3
|
||||
<input type="checkbox" value="4" />
|
||||
4
|
||||
<input type="checkbox" value="5" />
|
||||
5
|
||||
<input type="checkbox" value="6" />
|
||||
6
|
||||
<input type="checkbox" value="7" />
|
||||
7
|
||||
<input type="checkbox" value="8" />
|
||||
8
|
||||
<input type="checkbox" value="9" />
|
||||
9
|
||||
<input type="checkbox" value="10" />
|
||||
10
|
||||
<input type="checkbox" value="11" />
|
||||
11
|
||||
<input type="checkbox" value="12" />
|
||||
12
|
||||
</div>
|
||||
</div>
|
||||
<div title="周">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="week" onclick="everyTime(this)" />
|
||||
周 允许的通配符[, - * / L #]
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="week" onclick="unAppoint(this)" />
|
||||
不指定
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="week" onclick="startOn(this)" />
|
||||
周期 从星期
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:7" id="weekStart_0" value="1" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:2,max:7" value="2" id="weekEnd_0" />
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="week" onclick="weekOfDay(this)" />
|
||||
第
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:4" value="1" id="weekStart_1" />
|
||||
周 的星期
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:7" id="weekEnd_1" value="1" />
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="week" onclick="lastWeek(this)" />
|
||||
本月最后一个星期
|
||||
<input class="numberspinner" style="width: 60px" data-options="min:1,max:7" id="weekStart_2" value="1" />
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="week" id="week_appoint" />
|
||||
指定
|
||||
</div>
|
||||
<div class="imp weekList">
|
||||
<input type="checkbox" value="1" />
|
||||
1
|
||||
<input type="checkbox" value="2" />
|
||||
2
|
||||
<input type="checkbox" value="3" />
|
||||
3
|
||||
<input type="checkbox" value="4" />
|
||||
4
|
||||
<input type="checkbox" value="5" />
|
||||
5
|
||||
<input type="checkbox" value="6" />
|
||||
6
|
||||
<input type="checkbox" value="7" />
|
||||
7
|
||||
</div>
|
||||
</div>
|
||||
<div title="年">
|
||||
<div class="line">
|
||||
<input type="radio" checked="checked" name="year" onclick="unAppoint(this)" />
|
||||
不指定 允许的通配符[, - * /] 非必填
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="year" onclick="everyTime(this)" />
|
||||
每年
|
||||
</div>
|
||||
<div class="line">
|
||||
<input type="radio" name="year" onclick="cycle(this)" />
|
||||
周期 从
|
||||
<input class="numberspinner" style="width: 90px" data-options="min:2018,max:3000" id="yearStart_0" value="2018" />
|
||||
-
|
||||
<input class="numberspinner" style="width: 90px" data-options="min:2020,max:3000" id="yearEnd_0" value="2020" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div data-options="region:'south',border:false" style="height: 250px">
|
||||
<fieldset style="border-radius: 3px; height: 220px">
|
||||
<legend>表达式</legend>
|
||||
<table style="height: 100px">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td align="center">秒</td>
|
||||
<td align="center">分钟</td>
|
||||
<td align="center">小时</td>
|
||||
<td align="center">日</td>
|
||||
<td align="center">
|
||||
月
|
||||
<br />
|
||||
</td>
|
||||
<td align="center">星期</td>
|
||||
<td align="center">年</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>表达式字段:</td>
|
||||
<td>
|
||||
<input type="text" name="v_second" class="col" value="*" readonly="readonly" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="v_min" class="col" value="*" readonly="readonly" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="v_hour" class="col" value="*" readonly="readonly" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="v_day" class="col" value="*" readonly="readonly" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="v_mouth" class="col" value="*" readonly="readonly" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="v_week" class="col" value="?" readonly="readonly" />
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" name="v_year" class="col" readonly="readonly" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Cron 表达式:</td>
|
||||
<td colspan="6"><input type="text" name="cron" style="width: 100%" value="* * * * * ?" id="cron" /></td>
|
||||
<td><input type="button" value="反解析到UI " id="btnFan" onclick="btnFan()" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="8">最近5次运行时间:</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="8" id="runTime"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div></div>
|
||||
</div>
|
||||
<script src="${base}/assets/platform/plugins/jquery/jquery.js" type="text/javascript"></script>
|
||||
<script src="${base}/assets/platform/cron/js/cron.js" type="text/javascript"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,326 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<el-card shadow="never">
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="ti-plus"></i>
|
||||
新建任务
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-table :data="tableData" @sort-change="pageOrder" header-align="center" style="width: 100%">
|
||||
<el-table-column sortable prop="name" label="任务名称" header-align="center"></el-table-column>
|
||||
<el-table-column prop="jobClass" label="执行类" header-align="center" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="data" label="执行参数" header-align="center" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="cron" label="定时规则" header-align="center" align="center"></el-table-column>
|
||||
<el-table-column prop="exeAt" label="执行时间" header-align="center" align="center">
|
||||
<template slot-scope="scope">{{formatAt(scope.row.exeAt)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column sortable prop="disabled" label="启用状态" header-align="center" align="center">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="scope.row.disabled" class="fa fa-circle text-danger ml5"></i>
|
||||
<i v-if="!scope.row.disabled" class="fa fa-circle text-success ml5"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'enable',id:scope.row.id,name:scope.row.name,row:scope.row}">启用</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',id:scope.row.id,name:scope.row.name,row:scope.row}">
|
||||
禁用
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item divided :command="{type:'edit',id:scope.row.id}">修改</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',id:scope.row.id,name:scope.row.name}">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog title="新建任务" :visible.sync="addDialogVisible" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="name" label="任务名称">
|
||||
<el-input maxlength="100" placeholder="任务名称" v-model="formData.name" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="jobClass" label="执行类">
|
||||
<el-input placeholder="执行类" v-model="formData.jobClass" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="cron" label="执行周期">
|
||||
<el-input placeholder="Cron表达式" v-model="formData.cron" auto-complete="off" tabindex="3" type="text">
|
||||
<template slot="append">
|
||||
<a href="${base}/platform/sys/task/cron" style="color: #409eff" target="_blank">表达式生成器</a>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="data" label="执行参数(Json)">
|
||||
<el-input type="textarea" v-model="formData.data" placeholder="{}" tabindex="4"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="修改任务" :visible.sync="editDialogVisible" :close-on-click-modal="false" width="50%">
|
||||
<el-form :model="formData" ref="editForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item prop="name" label="任务名称">
|
||||
<el-input maxlength="100" placeholder="任务名称" v-model="formData.name" auto-complete="off" tabindex="1" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="jobClass" label="执行类">
|
||||
<el-input placeholder="执行类" v-model="formData.jobClass" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="cron" label="执行周期">
|
||||
<el-input placeholder="Cron表达式" v-model="formData.cron" auto-complete="off" tabindex="3" type="text">
|
||||
<template slot="append">
|
||||
<a href="${base}/platform/sys/task/cron" style="color: #409eff" target="_blank">表达式生成器</a>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="data" label="执行参数(Json)">
|
||||
<el-input type="textarea" v-model="formData.data" placeholder="{}" tabindex="4"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
tableData: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
formData: {
|
||||
id: ""
|
||||
},
|
||||
formRules: {
|
||||
name: [{ required: true, message: "任务名称", trigger: "blur" }],
|
||||
jobClass: [{ required: true, message: "执行类", trigger: "blur" }],
|
||||
cron: [{ required: true, message: "执行周期", trigger: "blur" }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageOrder: function (column) {
|
||||
//按字段排序
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange: function (val) {
|
||||
//页码更新操作
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange: function (val) {
|
||||
//分页大小更新操作
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData: function () {
|
||||
//加载分页数据
|
||||
var self = this
|
||||
$.post(
|
||||
base + "/platform/sys/task/data",
|
||||
self.pageForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.tableData = data.data.list
|
||||
self.pageForm.totalCount = data.data.totalCount
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
},
|
||||
formatAt: function (val) {
|
||||
if (val && val > 0) return moment(val).format("YYYY-MM-DD HH:mm:ss")
|
||||
return ""
|
||||
},
|
||||
doSearch: function () {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
openAdd: function () {
|
||||
var self = this
|
||||
this.addDialogVisible = true
|
||||
this.formData = {} //打开新增窗口,表单先清空
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
doAdd: function () {
|
||||
var self = this
|
||||
self.$refs["addForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
base + "/platform/sys/task/addDo",
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.addDialogVisible = false
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
self.pageData() //添加失败也可以入库,所以加载一下看看
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
doEdit: function () {
|
||||
var self = this
|
||||
self.$refs["editForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
base + "/platform/sys/task/editDo",
|
||||
self.formData,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.doSearch() //查询是带条件的,加载新建用户选择的单位用户数据
|
||||
//self.pageData();
|
||||
self.editDialogVisible = false
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
},
|
||||
dropdownCommand: function (command) {
|
||||
//监听下拉框事件
|
||||
var self = this
|
||||
if ("enable" == command.type || "disable" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/task/" + command.type + "/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
if ("disable" == command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" == command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("edit" == command.type) {
|
||||
$.post(
|
||||
base + "/platform/sys/task/edit/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.formData = data.data //加载后台表单数据
|
||||
self.editDialogVisible = true //打开编辑窗口
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
if ("delete" == command.type) {
|
||||
self.$confirm("此操作将删除 " + command.name, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: function (a, b) {
|
||||
if ("confirm" == a) {
|
||||
//确认后再执行
|
||||
$.post(
|
||||
base + "/platform/sys/task/delete/" + command.id,
|
||||
{},
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
self.doSearch()
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
created: function () {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,151 @@
|
||||
const branchUnionCadreAudit = {
|
||||
template: /*language=HTML*/ `
|
||||
<el-card shadow="never" class="mt10">
|
||||
<table-tool label="基层干部">
|
||||
<el-radio-group @change="doSearch" class="mr5" size="small" v-model="pageForm.audit">
|
||||
<el-radio-button label="true">已审核</el-radio-button>
|
||||
<el-radio-button label="false">未审核</el-radio-button>
|
||||
</el-radio-group>
|
||||
|
||||
<!-- <el-button size="small" @click="doAudit(true)" :disabled="!multipleSelection.length" type="primary"-->
|
||||
<!-- icon="el-icon-check">批量通过-->
|
||||
<!-- </el-button>-->
|
||||
<!-- <el-button size="small" @click="doAudit(false)" :disabled="!multipleSelection.length" type="danger"-->
|
||||
<!-- icon="el-icon-check">批量拒绝-->
|
||||
<!-- </el-button>-->
|
||||
</table-tool>
|
||||
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" ref="table" row-key="id"
|
||||
style="width: 100%" @selection-change="handleSelectionChange" height="65vh">
|
||||
<el-table-column type="selection" width="55"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" type="index" :index="indexMethod"
|
||||
label="序号" width="80px"></el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns"
|
||||
show-overflow-tooltip
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
>
|
||||
<template v-if="column.prop==='isJoin'" scope="{row:{isJoin}}">
|
||||
<span v-if="isJoin" class="text-info">加入</span>
|
||||
<span v-else class="text-danger">退出</span>
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop=='instanceState'">
|
||||
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
|
||||
size="small"></enum-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" fixed="right" width="180px" v-if="!pageForm.audit">
|
||||
<template scope="{row}">
|
||||
<template v-if="row.taskState === 10">
|
||||
<el-button @click="handleTaskAction(row, 1)" size="mini" type="primary">通过</el-button>
|
||||
<el-button @click="handleTaskAction(row, 2)" size="mini" type="danger">拒绝</el-button>
|
||||
</template>
|
||||
<el-button v-if="row.canRevoke" @click="openRevoke(row)" size="mini" type="danger">
|
||||
撤回
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-row class="el-pagination-container" style="margin-bottom: 0">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageForm.totalCount">
|
||||
</el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
audit: false
|
||||
},
|
||||
tableColumns: [
|
||||
{ prop: 'loginName', label: '工号' },
|
||||
{ prop: 'userName', label: '姓名' },
|
||||
{ prop: 'unionName', label: '所属工会' },
|
||||
{ prop: 'mobile', label: '联系方式' },
|
||||
{ prop: 'roleName', label: '职务' },
|
||||
{ prop: 'isJoin', label: '申请类型' },
|
||||
{ prop: "curTaskName", label: "当前节点" },
|
||||
{ prop: "instanceState", label: "流程状态" }
|
||||
],
|
||||
multipleSelection: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
doAudit(pass) {
|
||||
this.$confirm('是否确认提交?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const ids = JSON.stringify(this.multipleSelection.map(v => v.id))
|
||||
this.$axios.post('/platform/sys/union/cadreDoAudit', {ids, pass})
|
||||
.then(resp => {
|
||||
if (resp.code === 0) {
|
||||
this.pageData()
|
||||
this.$refs.table.clearSelection();
|
||||
this.$message.success(resp.msg)
|
||||
}else{
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
},
|
||||
|
||||
handleTaskAction(row, val) {
|
||||
const msg = val === 1 ? "通过" : "拒绝"
|
||||
this.$confirm("您确定要" + msg + "吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
const loading = createLoading()
|
||||
this.formData = {
|
||||
origin: row.origin,
|
||||
processTaskId: row.taskId,
|
||||
taskName: row.curTaskName
|
||||
}
|
||||
this.$axios.post("/flow/common/executeTask", {
|
||||
data: JSON.stringify({
|
||||
...this.formData,
|
||||
submitType: val
|
||||
})
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
})
|
||||
})
|
||||
},
|
||||
|
||||
pageData() {
|
||||
this.$axios.post('/platform/sys/union/cadrePageData', this.pageForm)
|
||||
.then(resp => {
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
const branchUnionCommitteeMemberManage = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row :gutter="10" type="flex">
|
||||
<el-select placeholder="请选择届次" v-model="pageForm.sessionId" style="width: 15%"
|
||||
size="small">
|
||||
<el-option v-for="item in sessionOptions" :label="item.fullName" :value="item.id"
|
||||
:key="item.id"></el-option>
|
||||
</el-select>
|
||||
<el-input placeholder="请输入姓名或工号" clearable size="small"
|
||||
style="width: 300px;margin-left: 10px;"
|
||||
v-model="pageForm.searchKeyword"></el-input>
|
||||
|
||||
<el-button type="primary" class="ml5" size="small" icon="el-icon-search"
|
||||
@click="doSearch"></el-button>
|
||||
<el-button @click="openAdd" icon="ti-plus" type="primary" size="small" style="margin-left: auto">
|
||||
添加委员
|
||||
</el-button>
|
||||
<el-button @click="deleteRole" icon="el-icon-delete" type="danger" size="small" style="margin-left: 10px">
|
||||
删除当前届次委员角色
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" border>
|
||||
<el-table-column label="序号" type="index" width="50" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="loginName" label="工号"></el-table-column>
|
||||
<el-table-column prop="userName" label="姓名"></el-table-column>
|
||||
<!-- <el-table-column prop="mobile" label="联系方式"></el-table-column>-->
|
||||
<el-table-column prop="position" label="职务"></el-table-column>
|
||||
<el-table-column prop="sessionName" label="届次"></el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
<template scope="scope">
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete"
|
||||
@click="doDelete(scope.row.id)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container mt20">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="添加" :visible.sync="dialogFormVisible" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="formData" ref="formRef" size="small" label-width="80px" :rules="formRules">
|
||||
<el-form-item prop="sessionId" label="届次">
|
||||
<el-select placeholder="请选择届次" v-model="formData.sessionId" clearable style="width: 100%">
|
||||
<el-option v-for="item in sessionOptions" :label="item.fullName" :value="item.id"
|
||||
:key="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="userId" label="人员">
|
||||
<user-select
|
||||
v-model="formData.userId"
|
||||
style="width: 100%"
|
||||
api="/platform/sys/committeeMember/listUserSelect"
|
||||
:api_params="{ sessionId: pageForm.sessionId }"
|
||||
:option_label_func="
|
||||
(item) => {
|
||||
return item.username + item.loginname + '(' + item.unitName + ')'
|
||||
}
|
||||
"
|
||||
></user-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="position" label="职务">
|
||||
<el-input v-model="formData.position" placeholder="请输入职务"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
formData: {},
|
||||
formRules: {
|
||||
sessionId: [{required: true, message: "必填", trigger: ["change", "blur"]}],
|
||||
userId: [{required: true, message: "必填", trigger: ["change", "blur"]}],
|
||||
},
|
||||
sessionOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
listSession() {
|
||||
this.$axios.post("/platform/teacherCongress/common/listSession").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.sessionOptions = res.data
|
||||
if (this.sessionOptions.length > 0) {
|
||||
this.$set(this.pageForm, "sessionId", this.sessionOptions[0].id)
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
$.get("/platform/sys/committeeMember/pageData", this.pageForm).then((res) => {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
})
|
||||
},
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
const session = this.sessionOptions.find(item => item.id === this.formData.sessionId)
|
||||
this.formData.sessionName = session.fullName
|
||||
$.post("/platform/sys/committeeMember/insert", this.formData).then((res) => {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
})
|
||||
})
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
$.post("/platform/sys/committeeMember/delete", {id}).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
})
|
||||
})
|
||||
},
|
||||
deleteRole() {
|
||||
this.$confirm("您当前操作委员信息不会删除,只会删除当前届次委员的角色,确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
$.post("/platform/sys/committeeMember/deleteRole", {sessionId:this.pageForm.sessionId}).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.listSession()
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
const branchUnionGroupManage = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row type="flex" style="column-gap: 10px">
|
||||
<el-input
|
||||
placeholder="可输入名称查询"
|
||||
size="small"
|
||||
style="width: 400px"
|
||||
clearable
|
||||
v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch"
|
||||
></el-input>
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" style="margin-left: auto"
|
||||
@click="openAdd">
|
||||
新增
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" :height="table_height ? Math.max(200, table_height - 90) : null" border>
|
||||
<el-table-column type="index" width="50" label="序号"></el-table-column>
|
||||
<el-table-column prop="code" label="小组编码" width="120px"></el-table-column>
|
||||
<el-table-column prop="name" label="小组名称" width="200px"></el-table-column>
|
||||
<el-table-column prop="leaderInfos" label="组长信息"></el-table-column>
|
||||
<el-table-column prop="unitInfos" label="组成单位"></el-table-column>
|
||||
<el-table-column label="操作" width="200px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button type="primary" size="mini" @click="onEdit(row)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button type="danger" size="mini" @click="onDelete(row.id)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog :title="formData.id ? '编辑' : '新增'" :visible.sync="dialogFormVisible" width="600px"
|
||||
:close-on-click-modal="false">
|
||||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||
<el-form-item label="小组编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="小组编码" maxlength="10"
|
||||
show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="小组名称" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="小组名称" maxlength="100"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="组成单位" prop="unitIds">
|
||||
<el-select clearable filterable placeholder="请选择组成单位" multiple
|
||||
style="width: 100%" v-model="formData.unitIds">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in units"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="小组长" prop="leaders">
|
||||
<el-select
|
||||
v-model="formData.leaders"
|
||||
filterable
|
||||
remote multiple
|
||||
reserve-keyword
|
||||
placeholder="请输入姓名查询"
|
||||
:remote-method="getUserOptions"
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
:label="item.username+'('+item.unitName+')'"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
props: {
|
||||
union_id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 工会小组列表包含分页,父页面传入高度后预留分页区域。
|
||||
table_height: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {},
|
||||
formData: {},
|
||||
rules: {
|
||||
code: [{ required: true, message: "请输入小组编码", trigger: "blur" }],
|
||||
name: [{ required: true, message: "请输入小组名称", trigger: "blur" }],
|
||||
leaders: [{ required: true, message: "请选择小组长", trigger: "change" }],
|
||||
unitIds: [{ required: true, message: "请选择组成单位", trigger: "change" }],
|
||||
},
|
||||
userOptions: [],
|
||||
units: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
await this.getUnits()
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
this.$refs.formRef.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
async onEdit(row) {
|
||||
this.formData = { ...row }
|
||||
this.formData.unitIds = JSON.parse(row.unitIds)
|
||||
this.formData.leaders = JSON.parse(row.leaders)
|
||||
await this.getUnits(row.id)
|
||||
this.getUserOptions(null, this.formData.leaders)
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.formRef.clearValidate()
|
||||
})
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (valid) {
|
||||
const { id } = this.formData
|
||||
const url = "/platform/sys/unionGroup" + (id ? "/update" : "/insert")
|
||||
const formData = clone(this.formData)
|
||||
formData.unitIds = JSON.stringify(formData.unitIds)
|
||||
formData.leaders = JSON.stringify(formData.leaders)
|
||||
$.post(url, { ...formData, unionId: this.union_id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.dialogFormVisible = false
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onDelete(id) {
|
||||
this.$confirm("您确定要删除吗, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
$.post("/platform/sys/unionGroup/delete/" + id).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
},
|
||||
|
||||
pageData() {
|
||||
$.get("/platform/sys/unionGroup/pageData", { ...this.pageForm, unionId: this.union_id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getUserOptions(keyword, userIds = null) {
|
||||
$.get("/platform/sys/unionGroup/listUser", {
|
||||
keyword,
|
||||
unionId: this.union_id,
|
||||
userIds: JSON.stringify(userIds)
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.userOptions = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
async getUnits(val) {
|
||||
const resp = await this.$axios.post("/platform/sys/unionGroup/getUnits", {
|
||||
unionId: this.union_id,
|
||||
groupId: val
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.units = resp.data
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
const branchUnionManage = {
|
||||
template: `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row :gutter="10" type="flex">
|
||||
<el-input placeholder="请输入分工会名称" clearable size="small" style="width: 300px" v-model="pageForm.searchKeyword"></el-input>
|
||||
|
||||
<el-button type="primary" class="ml5" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
<el-button @click="openAudit" icon="el-icon-s-check" type="primary" style="margin-left: auto" size="small" v-if="$auth.hasRoleOr(['SYSADMIN','SCHOOL_UNION_ADMIN'])">基层干部审核</el-button>
|
||||
<el-button @click="openAdd" icon="ti-plus" type="primary" size="small" v-if="$auth.hasPermission('sys.manager.union.add')">新建分工会</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" border>
|
||||
<el-table-column label="序号" type="index" width="50" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="name" label="工会名称" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="unionCode" label="工会编码"></el-table-column>
|
||||
<el-table-column prop="campus" label="校区"></el-table-column>
|
||||
<el-table-column prop="chairman" label="分工会主席" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" width="250" v-if="$auth.hasPermissionOr(['sys.manager.union.edit','sys.manager.union.delete'])">
|
||||
<template scope="scope">
|
||||
<!-- <el-button v-if="$auth.hasPermission('sys.manager.union.branchOfficer')" type="primary" size="mini">设置干部</el-button>-->
|
||||
<el-button v-if="$auth.hasPermission('sys.manager.union.edit')" type="primary" size="mini" @click="openEdit(scope.row.id)">编辑</el-button>
|
||||
<el-button v-if="$auth.hasPermission('sys.manager.union.delete')" type="danger" size="mini" @click="doDelete(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container mt20">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-dialog :title="formData.id ? '编辑分工会' : '新增分工会'" :visible.sync="dialogFormVisible" width="600px">
|
||||
<el-form :model="formData" ref="formRef" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item label="工会名称" prop="name">
|
||||
<el-input v-model="formData.name" maxlength="100" placeholder="工会名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="工会编码" prop="unionCode">
|
||||
<el-input v-model="formData.unionCode" maxlength="100" placeholder="工会编码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="校区" prop="campus">
|
||||
<dict-select v-model="formData.campus" code="USER_CAMPUS"></dict-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
formData: {},
|
||||
formRules: {
|
||||
name: [{ required: true, message: "请输入工会名称", trigger: ["change", "blur"] }],
|
||||
unionCode: [{ required: true, message: "请输入工会编码", trigger: ["change", "blur"] }],
|
||||
campus: [{ required: false, message: "请选择校区", trigger: ["change", "blur"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
$.get("/platform/sys/union/pageData", this.pageForm).then((res) => {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
})
|
||||
},
|
||||
// 基层干部审核
|
||||
openAudit() {
|
||||
this.$emit('union-cadre')
|
||||
},
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
})
|
||||
},
|
||||
openEdit(id) {
|
||||
this.dialogFormVisible = true
|
||||
$.get("/platform/sys/union/findOne", { id }).then((res) => {
|
||||
this.formData = res.data
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (!valid) return
|
||||
$.post("/platform/sys/union/" + (this.formData.id ? "update" : "insert"), this.formData).then((res) => {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
this.$emit("refresh", null)
|
||||
})
|
||||
})
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
$.post("/platform/sys/union/doDelete", { id }).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.pageData()
|
||||
this.$emit("refresh", null)
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
const branchUnionPartUnitManage = {
|
||||
template: `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row type="flex" style="column-gap: 10px">
|
||||
<el-input
|
||||
placeholder="请输入单位编码或单位名称查询"
|
||||
size="small"
|
||||
style="width: 400px"
|
||||
clearable
|
||||
v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch"
|
||||
></el-input>
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-setting" style="margin-left: auto" @click="openAdd" v-if="$auth.hasPermission('sys.manager.union.partUnit')">设置组成单位</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" :height="table_height ? Math.max(200, table_height - 90) : null" border>
|
||||
<el-table-column type="index" width="50" label="序号" :index="indexMethod"></el-table-column>
|
||||
<el-table-column prop="unitcode" label="单位编码"></el-table-column>
|
||||
<el-table-column prop="name" label="单位名称"></el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="设置组成单位" :visible.sync="dialogFormVisible" width="1000px" :close-on-click-modal="false">
|
||||
<el-transfer
|
||||
ref="transferRef"
|
||||
:titles="['可选二级单位', '当前成员单位']"
|
||||
filterable
|
||||
:props="{
|
||||
key: 'id',
|
||||
label: 'name'
|
||||
}"
|
||||
:filter-method="filterUnit"
|
||||
v-model="selectUnitsIds"
|
||||
:data="allUnits"
|
||||
></el-transfer>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
props: {
|
||||
union_id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 父页面可传入可用高度;默认值保持组件在其他页面的原有展示能力。
|
||||
table_height: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
selectUnitsIds: [],
|
||||
allUnits: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
union_id(val) {
|
||||
// this.doSearch()
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
if (this.$refs.transferRef) {
|
||||
this.$refs.transferRef.clearQuery("left")
|
||||
this.$refs.transferRef.clearQuery("right")
|
||||
}
|
||||
$.get("/platform/sys/union/branchUnionPartUnitTransferData", { unionId: this.union_id }).then((res) => {
|
||||
this.selectUnitsIds = res.data.selectUnitIds
|
||||
this.allUnits = res.data.allUnits
|
||||
})
|
||||
},
|
||||
|
||||
filterUnit(query, item) {
|
||||
return item.name.indexOf(query) > -1
|
||||
},
|
||||
|
||||
doSubmit() {
|
||||
$.post("/platform/sys/union/branchUnionPartUnitSet", {
|
||||
unitIds: JSON.stringify(this.selectUnitsIds),
|
||||
unionId: this.union_id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
pageData() {
|
||||
$.get("/platform/sys/union/branchUnionPartUnitPageData", { ...this.pageForm, unionId: this.union_id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
const branchUnionUserManage = {
|
||||
template:
|
||||
/*language=HTML*/
|
||||
`
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row type="flex" style="column-gap: 10px">
|
||||
<el-input
|
||||
placeholder="请输入内容"
|
||||
size="small"
|
||||
style="width: 400px"
|
||||
clearable
|
||||
v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch"
|
||||
>
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" size="small" placeholder="查询类型"
|
||||
style="width: 80px !important;">
|
||||
<el-option label="角色" value="roleName"></el-option>
|
||||
<el-option label="姓名" value="username"></el-option>
|
||||
<el-option label="工号" value="loginname"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
<el-select
|
||||
v-model="pageForm.j"
|
||||
placeholder="届数"
|
||||
size="small"
|
||||
clearable
|
||||
class="j-search-select"
|
||||
@change="handleJChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in jOptions"
|
||||
:key="item.code"
|
||||
:label="item.name"
|
||||
:value="item.code"
|
||||
>
|
||||
<span>{{ item.name }}</span>
|
||||
<span v-if="hasCadreData(item.code)" class="j-used-mark">*</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" style="margin-left: auto"
|
||||
@click="openAdd" v-if="$auth.hasPermission('sys.manager.union.branchOfficer')">添加
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" :height="table_height ? Math.max(200, table_height - 40) : null" border size="small">
|
||||
<el-table-column prop="loginName" label="工号"></el-table-column>
|
||||
<el-table-column prop="userName" label="姓名"></el-table-column>
|
||||
<el-table-column prop="mobile" label="联系方式"></el-table-column>
|
||||
<el-table-column label="届数">
|
||||
<template scope="{row}">
|
||||
{{ row.jName || row.j }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="roleName" label="职务"></el-table-column>
|
||||
<el-table-column prop="displayStatus" label="当前状态"></el-table-column>
|
||||
<el-table-column label="操作" width="150px"
|
||||
v-if="$auth.hasPermission('sys.manager.union.branchOfficer')">
|
||||
<template scope="scope">
|
||||
<el-button
|
||||
v-if="!isLeft(scope.row)"
|
||||
size="mini"
|
||||
type="warning"
|
||||
icon="el-icon-switch-button"
|
||||
@click="openLeave(scope.row)"
|
||||
></el-button>
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete"
|
||||
@click="doDelete(scope.row)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-dialog title="添加" :visible.sync="dialogFormVisible" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="formData" ref="form" size="small" label-width="80px">
|
||||
<el-form-item prop="roleCode" label="角色">
|
||||
<el-select v-model="formData.roleCode" placeholder="请选择角色" @change="handleRoleChange">
|
||||
<el-option v-for="item in roleOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="isUnitPartySecretary()" prop="unitIds" label="所属单位">
|
||||
<el-select v-model="formData.unitIds" multiple collapse-tags placeholder="请选择单位">
|
||||
<el-option v-for="item in partySecretaryUnitOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="j" label="届数">
|
||||
<dict-select v-model="formData.j" code="TEACHER_CONGRESS_J" placeholder="请选择届数"></dict-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="userId" label="人员">
|
||||
<user-select
|
||||
v-model="formData.userId"
|
||||
style="width: 100%"
|
||||
api="/platform/sys/union/listUnion"
|
||||
:api_params="{ unionId: union_id }"
|
||||
:option_label_func="
|
||||
(item) => {
|
||||
return item.username + item.loginname + '(' + item.unitName + ')'
|
||||
}
|
||||
"
|
||||
></user-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog title="离任" :visible.sync="leaveDialogVisible" width="420px" :close-on-click-modal="false">
|
||||
<el-form :model="leaveForm" ref="leaveForm" size="small" label-width="90px">
|
||||
<el-form-item label="离任时间" required>
|
||||
<el-date-picker
|
||||
v-model="leaveForm.leaveDate"
|
||||
type="date"
|
||||
value-format="yyyy-MM-dd"
|
||||
placeholder="请选择离任时间"
|
||||
style="width: 100%"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="leaveDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doLeave">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
props: {
|
||||
union_id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 父页面可传入可用高度;默认值保持组件在其他页面的原有展示能力。
|
||||
table_height: {
|
||||
type: Number,
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
searchName: "username",
|
||||
j: ""
|
||||
},
|
||||
dialogFormVisible: false,
|
||||
formData: {},
|
||||
roleOptions: [],
|
||||
partySecretaryUnitOptions: [],
|
||||
leaveDialogVisible: false,
|
||||
leaveForm: {
|
||||
id: "",
|
||||
leaveDate: ""
|
||||
},
|
||||
jOptions: [],
|
||||
usedJCodes: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
union_id(val) {
|
||||
this.pageForm.j = ""
|
||||
this.initJSearch()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
loadJOptions() {
|
||||
return $.get("/open/common/dictOptions", { code: "TEACHER_CONGRESS_J" }).then((res) => {
|
||||
this.jOptions = res.data || []
|
||||
})
|
||||
},
|
||||
loadUsedJCodes() {
|
||||
if (!this.union_id) {
|
||||
this.usedJCodes = []
|
||||
return $.Deferred().resolve().promise()
|
||||
}
|
||||
return $.get("/platform/sys/union/branchUnionUserUsedJData", {
|
||||
unionId: this.union_id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.usedJCodes = res.data || []
|
||||
}
|
||||
})
|
||||
},
|
||||
hasCadreData(code) {
|
||||
return this.usedJCodes.includes(code)
|
||||
},
|
||||
getHighestUsedJ() {
|
||||
if (!this.usedJCodes.length) {
|
||||
return ""
|
||||
}
|
||||
for (let i = this.jOptions.length - 1; i >= 0; i--) {
|
||||
if (this.usedJCodes.includes(this.jOptions[i].code)) {
|
||||
return this.jOptions[i].code
|
||||
}
|
||||
}
|
||||
return this.usedJCodes[0]
|
||||
},
|
||||
initJSearch() {
|
||||
const optionTask = this.jOptions.length ? $.Deferred().resolve().promise() : this.loadJOptions()
|
||||
$.when(optionTask, this.loadUsedJCodes()).then(() => {
|
||||
// 页面初始化时不限定届次,默认展示该分工会所有届次的干部。
|
||||
this.pageForm.j = ""
|
||||
this.pageData()
|
||||
})
|
||||
},
|
||||
handleJChange(val) {
|
||||
this.pageForm.j = val || ""
|
||||
this.doSearch()
|
||||
},
|
||||
isLeft(row) {
|
||||
return row.isServing === false || row.isServing === 0 || row.displayStatus === "离任"
|
||||
},
|
||||
isUnitPartySecretary() {
|
||||
return this.formData.roleCode === "UNIT_PARTY_SECRETARY"
|
||||
},
|
||||
loadRoleOptions() {
|
||||
return $.get("/platform/sys/union/branchUnionRoleOptions").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, "roleOptions", res.data || [])
|
||||
}
|
||||
})
|
||||
},
|
||||
loadPartySecretaryUnitOptions() {
|
||||
return $.get("/platform/sys/union/branchUnionPartySecretaryUnitOptions", {
|
||||
unionId: this.union_id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this, "partySecretaryUnitOptions", res.data || [])
|
||||
// 新增二级党委书记时,默认授权当前分工会下的全部二级单位。
|
||||
this.$set(this.formData, "unitIds", this.partySecretaryUnitOptions.map(item => item.id))
|
||||
}
|
||||
})
|
||||
},
|
||||
handleRoleChange() {
|
||||
if (this.isUnitPartySecretary()) {
|
||||
this.loadPartySecretaryUnitOptions()
|
||||
return
|
||||
}
|
||||
this.$set(this.formData, "unitIds", [])
|
||||
},
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
})
|
||||
},
|
||||
openLeave(row) {
|
||||
this.leaveForm = {
|
||||
id: row.id,
|
||||
leaveDate: ""
|
||||
}
|
||||
this.leaveDialogVisible = true
|
||||
},
|
||||
doSubmit() {
|
||||
if (!this.formData.j) {
|
||||
this.$message.error("请选择届数")
|
||||
return
|
||||
}
|
||||
if (this.isUnitPartySecretary() && (!this.formData.unitIds || this.formData.unitIds.length === 0)) {
|
||||
this.$message.error("请选择所属单位")
|
||||
return
|
||||
}
|
||||
const submitUrl = this.isUnitPartySecretary()
|
||||
? "/platform/sys/union/insertBranchUnionPartySecretaryRole"
|
||||
: "/platform/sys/union/insertBranchUnionUserRole"
|
||||
$.post(submitUrl, {
|
||||
...this.formData,
|
||||
unitIds: JSON.stringify(this.formData.unitIds || []),
|
||||
unionId: this.union_id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.loadUsedJCodes()
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
doLeave() {
|
||||
if (!this.leaveForm.leaveDate) {
|
||||
this.$message.error("请选择离任时间")
|
||||
return
|
||||
}
|
||||
$.post("/platform/sys/union/leaveBranchUnionUserRole", this.leaveForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.leaveDialogVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete({ userId, roleCode }) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then((res) => {
|
||||
$.post("/platform/sys/union/deleteBranchUnionUserRole", {
|
||||
userId,
|
||||
roleCode,
|
||||
unionId: this.union_id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.loadUsedJCodes()
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
$.get("/platform/sys/union/branchUnionUserPageData", {
|
||||
...this.pageForm,
|
||||
unionId: this.union_id,
|
||||
j: this.pageForm.j || ""
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadRoleOptions()
|
||||
this.initJSearch()
|
||||
},
|
||||
style: /*language=CSS*/ `
|
||||
/deep/ .el-select {
|
||||
width: 100% !important;
|
||||
}
|
||||
/deep/ .j-search-select {
|
||||
width: 80px !important;
|
||||
}
|
||||
.j-used-mark {
|
||||
color: #f56c6c;
|
||||
margin-left: 4px;
|
||||
font-weight: 700;
|
||||
}
|
||||
`
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-row type="flex" :gutter="20" style="height: calc(100vh - 126px)">
|
||||
<el-col :span="5">
|
||||
<el-card shadow="never" style="height: 100%" body-style="{ height: '100%' }">
|
||||
<el-input placeholder="输入关键字进行查找" v-model="filterText" clearable></el-input>
|
||||
<div style="max-height: calc(100vh - 200px); overflow-y: auto">
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
ref="treeRef"
|
||||
:expand-on-click-node="false"
|
||||
:props="{
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}"
|
||||
default-expand-all
|
||||
@node-click="treeNodeClick"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
>
|
||||
<template slot-scope="{ node, data }">
|
||||
<span class="el-tree-node__label">
|
||||
<i class="el-icon-folder-opened" v-if="node.level===1"></i>
|
||||
<i class="el-icon-folder" v-else></i>
|
||||
{{node.label}}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-card shadow="never">
|
||||
<el-tabs v-model="schoolUnionTabActive" type="card" v-if="currentTreeNode==null || currentTreeNode.level===1">
|
||||
<el-tab-pane name="branchUnionManage">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
分工会信息
|
||||
</span>
|
||||
<sys-union-branch-union-manage @refresh="getTreeData()" @union-cadre="unionCadreInit"></sys-union-branch-union-manage>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="schoolUnionInfo" v-if="$auth.hasPermission('sys.manager.union.schoolOfficer')">
|
||||
<span slot="label">
|
||||
<i class="el-icon-office-building"></i>
|
||||
校工会信息
|
||||
</span>
|
||||
<sys-union-school-union-user-manage></sys-union-school-union-user-manage>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="unionCommitteeMemberManage" v-if="$auth.hasPermission('sys.manager.union.committeeMember')">
|
||||
<span slot="label">
|
||||
<i class="el-icon-office-building"></i>
|
||||
工会委员信息
|
||||
</span>
|
||||
<sys-union-committee-member-manage></sys-union-committee-member-manage>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-tabs v-model="branchUnionTabActive" @tab-click="branchTabChange" type="card" v-if="currentTreeNode && currentTreeNode.level===2">
|
||||
<el-tab-pane name="branchUnionUserManage">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
分工会干部
|
||||
</span>
|
||||
<sys-union-branch-union-user-manage
|
||||
ref="branchUnionUserManageRef"
|
||||
:union_id="currentTreeData.id"
|
||||
></sys-union-branch-union-user-manage>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="branchUnionPartUnitManage">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
组成单位
|
||||
</span>
|
||||
<sys-union-branch-union-part-unit-manage
|
||||
ref="branchUnionPartUnitManageRef"
|
||||
:union_id="currentTreeData.id"
|
||||
></sys-union-branch-union-part-unit-manage>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="branchUnionGroup">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
工会小组
|
||||
</span>
|
||||
<sys-union-branch-union-group-manage
|
||||
ref="branchUnionGroupRef"
|
||||
:union_id="currentTreeData.id"
|
||||
></sys-union-branch-union-group-manage>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<sys-union-branch-union-cadre-audit ref="unionCadreAuditRef">
|
||||
</sys-union-branch-union-cadre-audit>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("branchUnionManage.js"){}#-->
|
||||
<!--#include("schoolUnionUserManage.js"){}#-->
|
||||
<!--#include("branchUnionUserManage.js"){}#-->
|
||||
<!--#include("branchUnionPartUnitManage.js"){}#-->
|
||||
<!--#include("branchUnionGroupManage.js"){}#-->
|
||||
<!--#include("branchUnionCadreAudit.js"){}#-->
|
||||
<!--#include("branchUnionCommitteeMemberManage.js"){}#-->
|
||||
|
||||
new Vue({
|
||||
el: "#app",
|
||||
components: {
|
||||
"sys-union-branch-union-manage": branchUnionManage,
|
||||
"sys-union-school-union-user-manage": schoolUnionUserManage,
|
||||
"sys-union-branch-union-user-manage": branchUnionUserManage,
|
||||
"sys-union-branch-union-part-unit-manage": branchUnionPartUnitManage,
|
||||
"sys-union-branch-union-group-manage": branchUnionGroupManage,
|
||||
"sys-union-branch-union-cadre-audit": branchUnionCadreAudit,
|
||||
"sys-union-committee-member-manage": branchUnionCommitteeMemberManage,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
filterText: null,
|
||||
treeData: [],
|
||||
|
||||
currentTreeNode: null,
|
||||
currentTreeData: null,
|
||||
|
||||
schoolUnionTabActive: "branchUnionManage",
|
||||
branchUnionTabActive: "branchUnionUserManage"
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
filterText(val) {
|
||||
this.$refs.treeRef.filter(val)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
treeNodeClick(data, node) {
|
||||
this.currentTreeData = data
|
||||
this.currentTreeNode = node
|
||||
if (node.level === 2) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs[this.branchUnionTabActive + "Ref"].doSearch()
|
||||
})
|
||||
}
|
||||
},
|
||||
filterNode(value, data) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
},
|
||||
getTreeData() {
|
||||
$.get(loc() + "/tree").then((res) => {
|
||||
this.treeData = res.data
|
||||
})
|
||||
},
|
||||
branchTabChange(val) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs[val.name + "Ref"].doSearch()
|
||||
})
|
||||
},
|
||||
|
||||
// 基层干部审核初始化
|
||||
unionCadreInit() {
|
||||
this.$refs.guava.edit(() => {
|
||||
this.$refs.unionCadreAuditRef.pageData()
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTreeData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,110 @@
|
||||
const schoolUnionUserManage = {
|
||||
template:
|
||||
/*language=HTML*/
|
||||
`
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row type="flex" style="column-gap: 10px">
|
||||
<el-input
|
||||
placeholder="请输入内容"
|
||||
size="small"
|
||||
style="width: 400px"
|
||||
clearable
|
||||
v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch"
|
||||
>
|
||||
<el-select v-model="pageForm.searchName" size="small" slot="prepend" placeholder="查询类型"
|
||||
style="width: 120px">
|
||||
<el-option label="角色" value="roleName"></el-option>
|
||||
<el-option label="姓名" value="username"></el-option>
|
||||
<el-option label="工号" value="loginname"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" style="margin-left: auto"
|
||||
@click="openAdd">添加
|
||||
</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" border>
|
||||
<el-table-column prop="username" label="姓名"></el-table-column>
|
||||
<el-table-column prop="loginname" label="工号"></el-table-column>
|
||||
<el-table-column prop="roleName" label="角色"></el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
<template scope="scope">
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete"
|
||||
@click="doDelete(scope.row)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
<el-dialog title="添加" :visible.sync="dialogFormVisible" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="formData" ref="form" size="small" label-width="80px">
|
||||
<el-form-item prop="roleCode" label="角色">
|
||||
<dict-select v-model="formData.roleCode" code="SCHOOL_UNION_ROLES"></dict-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="userId" label="人员">
|
||||
<user-select v-model="formData.userId" api_input_key_name="query"></user-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
searchName: "username"
|
||||
},
|
||||
dialogFormVisible: false,
|
||||
formData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
$.post("/platform/sys/union/insertSchoolUnionUserRole", this.formData).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
},
|
||||
doDelete({ userId, roleCode }) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then((res) => {
|
||||
$.post("/platform/sys/union/deleteSchoolUnionUserRole", { userId, roleCode }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
$.get("/platform/sys/union/schoolUnionUserPageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style scoped>
|
||||
.custom-tree {
|
||||
background: transparent;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/* 节点行高调整 */
|
||||
.custom-tree .el-tree-node {
|
||||
margin: 2px 0;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
|
||||
/* 选中状态 */
|
||||
.custom-tree .el-tree-node.is-current > .el-tree-node__content {
|
||||
background: #e8f4ff;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
|
||||
/* 节点内容区域 */
|
||||
.custom-tree .el-tree-node__content {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
padding: 0 8px;
|
||||
border-radius: 6px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
/* 自定义节点内容 */
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
|
||||
/* 文件夹图标颜色优化 */
|
||||
.el-icon-folder {
|
||||
color: #909399;
|
||||
}
|
||||
.el-icon-folder-opened {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
/* 展开时图标旋转动画 */
|
||||
.custom-tree .el-tree-node__expand-icon {
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.custom-tree .el-tree-node__expand-icon.expanded {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* 节点标签 */
|
||||
.node-label {
|
||||
flex: 1;
|
||||
font-size: 13px;
|
||||
color: #303133;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 禁用展开图标(当无子节点时) */
|
||||
.custom-tree .el-tree-node__expand-icon.is-leaf {
|
||||
color: transparent;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="app" v-cloak>
|
||||
<el-row type="flex" :gutter="20" style="height: calc(100vh - 126px)">
|
||||
<el-col :span="5">
|
||||
<el-card shadow="never">
|
||||
<el-input placeholder="输入关键字进行查找" v-model="filterText" clearable></el-input>
|
||||
<div style="max-height: calc(100vh - 200px); overflow-y: auto">
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
ref="treeRef"
|
||||
:expand-on-click-node="false"
|
||||
:props="{ children: 'children', label: 'name' }"
|
||||
node-key="id"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
@node-click="treeNodeClick"
|
||||
:filter-node-method="filterNode"
|
||||
highlight-current
|
||||
class="custom-tree"
|
||||
>
|
||||
<template slot-scope="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<i class="el-icon-folder-opened" v-if="node.level===1"></i>
|
||||
<i class="el-icon-folder" v-else></i>
|
||||
<span class="node-label">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-card v-if="currentTreeData && currentTreeData.unitTypeCode==0" shadow="never">
|
||||
<unit-manage
|
||||
:current-Node="currentTreeNode?.level"
|
||||
:current-data="currentTreeData"
|
||||
:unit-level="2"
|
||||
@refresh="getTreeData()"
|
||||
ref="bzlRef"
|
||||
></unit-manage>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" v-if="currentTreeData && currentTreeData.unitTypeCode==1">
|
||||
<el-tabs v-model="unitTabActive" type="card">
|
||||
<el-tab-pane name="threeUnitManage">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
下设单位
|
||||
</span>
|
||||
<unit-manage
|
||||
:current-data="currentTreeData"
|
||||
:unit-level="3"
|
||||
:current-Node="currentTreeNode?.level"
|
||||
@refresh="getTreeData()"
|
||||
style="margin-top: 10px"
|
||||
ref="bmlRef"
|
||||
></unit-manage>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="unitUserManage">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
单位人员
|
||||
</span>
|
||||
<unit-user ref="userRef" :current-data="currentTreeData"></unit-user>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane class="tab" name="unitLeaderManage">
|
||||
<span slot="label">
|
||||
<i class="el-icon-school"></i>
|
||||
单位干部
|
||||
</span>
|
||||
<unit-leader-manage ref="leaderRef" :current-data="currentTreeData"></unit-leader-manage>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("unitUser.js"){}#-->
|
||||
<!--#include("unitManage.js"){}#-->
|
||||
<!--#include("unitLeader.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data: function () {
|
||||
return {
|
||||
unitTabActive: "threeUnitManage",
|
||||
filterText: null,
|
||||
currentTreeNode: null,
|
||||
currentTreeData: null,
|
||||
defaultExpandedKeys: [],
|
||||
treeData: []
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"unit-manage": UNIT_MANAGE_TEMPLATE,
|
||||
"unit-user": UNIT_USER_MANAGE_TEMPLATE,
|
||||
"unit-leader-manage": UNIT_LEADER_MANAGE_TEMPLATE
|
||||
},
|
||||
watch: {
|
||||
filterText(val) {
|
||||
this.$refs.treeRef.filter(val)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTreeData() {
|
||||
this.$axios.post(loc() + "/tree").then((res) => {
|
||||
this.treeData = res.data
|
||||
if (this.treeData) {
|
||||
this.currentTreeData = this.treeData[0]
|
||||
this.defaultExpandedKeys = this.treeData.length > 0 ? [this.treeData[0].id] : []
|
||||
}
|
||||
})
|
||||
},
|
||||
treeNodeClick(data, node) {
|
||||
this.currentTreeData = data
|
||||
this.currentTreeNode = node
|
||||
this.$nextTick(()=>{
|
||||
if (data.unitTypeCode==1) {
|
||||
this.$refs.bmlRef.pageData()
|
||||
this.$refs.userRef.pageData()
|
||||
this.$refs.leaderRef.pageData()
|
||||
} else {
|
||||
this.$refs.bzlRef.pageData()
|
||||
}
|
||||
})
|
||||
},
|
||||
filterNode(value, data, node) {
|
||||
if (!value) return true
|
||||
return data.name.indexOf(value) !== -1
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getTreeData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,153 @@
|
||||
const UNIT_LEADER_MANAGE_TEMPLATE = {
|
||||
template: `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row :gutter="10" type="flex">
|
||||
<el-input placeholder="请输入内容" size="small" v-model="pageForm.searchKeyword"
|
||||
clearable
|
||||
@keyup.enter.native="doSearch" style="width: 300px">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width: 80px">
|
||||
<el-option label="姓名" value="username"></el-option>
|
||||
<el-option label="工号" value="loginname"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
<el-button type="primary" class="ml5" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<table-tool label="人员列表">
|
||||
<el-button @click="openAdd" type="primary" size="small">新增</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" size="mini">
|
||||
<el-table-column label="序号" type="index" width="60">
|
||||
<template v-slot="scope">{{scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="姓名" prop="username"></el-table-column>
|
||||
<el-table-column label="工号" prop="loginname"></el-table-column>
|
||||
<el-table-column label="角色" prop="roleName"></el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="danger" @click="doDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
<el-dialog title="添加" :visible.sync="dialogFormVisible" width="600px" :close-on-click-modal="false">
|
||||
<el-form :model="formData" ref="form" size="small" label-width="60px">
|
||||
<el-form-item prop="roleCode" label="角色">
|
||||
<dict-select placeholder="请选择角色" v-model="formData.roleCode" code="UNIT_ROLES" @change="onRoleChange"></dict-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="userId" label="人员">
|
||||
<user-select
|
||||
placeholder="请选择人员"
|
||||
style="width: 100%"
|
||||
v-model="formData.userId"
|
||||
api="/platform/sys/unit/listUserSelect"
|
||||
:api_params="{ unitId: currentData?.id, roleCode: formData.roleCode }"
|
||||
:option_label_func="
|
||||
(item) => {
|
||||
return item.username + item.loginname + '(' + item.unitName + ')'
|
||||
}
|
||||
"
|
||||
></user-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
currentData: {
|
||||
type: Object,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
formData: {},
|
||||
pageForm: {
|
||||
unitName: "",
|
||||
searchName: "username",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
audit: false
|
||||
},
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
onRoleChange() {
|
||||
this.$set(this.formData, "userId", "")
|
||||
},
|
||||
doDelete({ userId, roleCode }) {
|
||||
this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then((res) => {
|
||||
$.post("/platform/sys/unit/deleteUnitUserRole", {
|
||||
userId,
|
||||
roleCode,
|
||||
unitId: this.currentData.id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
$.post("/platform/sys/unit/insertUnitUserRole", {
|
||||
...this.formData,
|
||||
unitId: this.currentData.id
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange(val) {
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData() {
|
||||
this.pageForm.unitId = this.currentData.id
|
||||
this.$axios.post(loc() + "/leaderPageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
const UNIT_MANAGE_TEMPLATE = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter);">
|
||||
<el-row :gutter="10" type="flex">
|
||||
<el-input placeholder="请输入单位名称" clearable size="small" style="width: 300px" v-model="pageForm.unitName"></el-input>
|
||||
<el-button type="primary" class="ml5" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<table-tool label="单位列表">
|
||||
<el-button @click="openAdd" type="primary" size="small">新建单位</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" size="mini">
|
||||
<el-table-column label="序号" type="index" width="60">
|
||||
<template v-slot="scope">{{scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位名称" prop="name"></el-table-column>
|
||||
<el-table-column label="单位代码" prop="unitcode" width="200"></el-table-column>
|
||||
<el-table-column label="单位等级" prop="unitLevel" width="100"></el-table-column>
|
||||
<el-table-column label="操作" width="200px">
|
||||
<template v-slot="scope">
|
||||
<el-button size="mini" type="primary" @click="openEdit(scope.row.id)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" @click="doDelete(scope.row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="设置单位" :visible.sync="dialogVisible" :close-on-click-modal="false" width="40%" top="2%">
|
||||
<el-form :model="formData" ref="form" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item label="上级单位">
|
||||
<el-input maxlength="100" disabled :value="currentData?.name" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="单位名称">
|
||||
<el-input maxlength="100" placeholder="请输入单位名称" v-model="formData.name" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="aliasName" label="单位别名">-->
|
||||
<!-- <el-input maxlength="100" placeholder="请输入单位别名" v-model="formData.aliasName" auto-complete="off" tabindex="3" type="text"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item prop="unitcode" label="单位编码">
|
||||
<el-input maxlength="100" placeholder="请输入单位编码" v-model="formData.unitcode" auto-complete="off" tabindex="4" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="unitLevel" label="单位等级">
|
||||
<el-select v-model="formData.unitLevel" placeholder="请输入单位等级" style="width: 100%">
|
||||
<el-option v-if="unitLevel === 1" label="一级单位" :value="1"></el-option>
|
||||
<el-option v-if="unitLevel === 2" label="二级单位" :value="2"></el-option>
|
||||
<el-option v-if="unitLevel === 3" label="三级单位" :value="3"></el-option>
|
||||
<el-option v-if="unitLevel === 4" label="四级单位" :value="4"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="unionId" label="所属工会">
|
||||
<el-select v-model="formData.unionId" placeholder="请选择所属工会" style="width: 100%">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id"
|
||||
v-for="item in unions"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item prop="address" label="单位地址">-->
|
||||
<!-- <el-input maxlength="100" placeholder="请输入单位地址" v-model="formData.address" auto-complete="off" tabindex="5" type="text"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item prop="telephone" label="联系电话">-->
|
||||
<!-- <el-input maxlength="100" placeholder="请输入联系电话" v-model="formData.telephone" auto-complete="off" tabindex="6" type="text"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item prop="email" label="单位邮箱">-->
|
||||
<!-- <el-input maxlength="100" placeholder="请输入单位邮箱" v-model="formData.email" auto-complete="off" tabindex="7" type="text"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item prop="website" label="单位网站">-->
|
||||
<!-- <el-input maxlength="100" placeholder="请输入单位网站" v-model="formData.website" auto-complete="off" tabindex="8" type="text"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
<!-- <el-form-item prop="note" label="单位介绍">-->
|
||||
<!-- <el-input type="textarea" v-model="formData.note" placeholder="请输入单位介绍"></el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doHandle">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
currentData: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
currentNode: {
|
||||
type: Number,
|
||||
required: false
|
||||
},
|
||||
unitLevel: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
unitName: "",
|
||||
searchName: "",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
audit: false
|
||||
},
|
||||
tableData: [],
|
||||
dialogVisible: false,
|
||||
formData: {},
|
||||
formRules: {
|
||||
name: [{ required: true, message: "必填", trigger: "blur" }],
|
||||
unitcode: [{ required: true, message: "必填", trigger: "blur" }],
|
||||
unitLevel: [{ required: false, message: "必填", trigger: "blur" }],
|
||||
email: [
|
||||
{ required: false, message: "单位邮箱", trigger: "blur" },
|
||||
{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }
|
||||
]
|
||||
},
|
||||
options: [],
|
||||
parentUnit: [],
|
||||
|
||||
unions: [],
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
openAdd() {
|
||||
this.dialogVisible = true
|
||||
this.formData = {} //打开新增窗口,表单先清空
|
||||
this.parentUnit = []
|
||||
this.options = []
|
||||
},
|
||||
doHandle() {
|
||||
this.$confirm("确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
const self = this
|
||||
const url = this.formData.id ? "/platform/sys/unit/editDo" : "/platform/sys/unit/addDo"
|
||||
this.$refs["form"].validate(async function (valid) {
|
||||
if (valid) {
|
||||
if (self.currentNode) {
|
||||
self.formData.parentId = self.currentData.id
|
||||
}
|
||||
const resp = await self.$axios.post(url, self.formData)
|
||||
if (resp.code === 0) {
|
||||
self.$message.success(resp.msg)
|
||||
self.doSearch()
|
||||
self.dialogVisible = false
|
||||
self.$emit("refresh", null)
|
||||
} else {
|
||||
self.$message.warning(resp.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
async openEdit(id) {
|
||||
console.log(this.currentData)
|
||||
const resp = await this.$axios.post("/platform/sys/unit/edit/" + id)
|
||||
if (resp.code === 0) {
|
||||
this.formData = resp.data
|
||||
this.dialogVisible = true
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
async doDelete(id) {
|
||||
const confirm = await this.$confirm("此操作将永久删除, 是否继续?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
const resp = await this.$axios.post("/platform/sys/unit/delete/" + id)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
this.$emit("refresh", null)
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}
|
||||
},
|
||||
pageNumberChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange(val) {
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData() {
|
||||
this.pageForm.unitId = this.currentData.id
|
||||
// this.pageForm.unitLevel = this.unitLevel
|
||||
this.$axios.post(loc() + "/pageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.pageData()
|
||||
|
||||
if (this.$auth.hasRoleOr('SYSADMIN, SCHOOL_UNION_ADMIN')) {
|
||||
this.unions = await this.$businessTool.listUnion()
|
||||
} else {
|
||||
this.unions = await this.$businessTool.listUnion(this.store.state.user.union.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
const UNIT_USER_MANAGE_TEMPLATE = {
|
||||
template: /*language=HTML*/ `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row :gutter="10" type="flex">
|
||||
<el-input placeholder="请输入内容" size="small" v-model="pageForm.searchKeyword"
|
||||
clearable
|
||||
@keyup.enter.native="doSearch" style="width: 300px">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width: 80px">
|
||||
<el-option label="姓名" value="username"></el-option>
|
||||
<el-option label="工号" value="loginname"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
<el-button type="primary" class="ml5" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<table-tool label="人员列表">
|
||||
|
||||
</table-tool>
|
||||
<el-table :data="tableData" size="mini">
|
||||
<el-table-column label="序号" type="index" width="60">
|
||||
<template v-slot="scope">{{scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="姓名" prop="username"></el-table-column>
|
||||
<el-table-column label="工号" prop="loginname"></el-table-column>
|
||||
<el-table-column label="性别" prop="sex"></el-table-column>
|
||||
<el-table-column label="联系方式" prop="mobile"></el-table-column>
|
||||
<el-table-column label="是否会员" prop="member">
|
||||
<template v-slot="scope">
|
||||
<el-tag v-if="scope.row.member" type="success">是</el-tag>
|
||||
<el-tag v-else type="danger">否</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</div>
|
||||
`,
|
||||
props: {
|
||||
currentData: {
|
||||
type: Object,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
unitName: "",
|
||||
searchName: "username",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: "",
|
||||
audit: false
|
||||
},
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
pageNumberChange(val) {
|
||||
this.pageForm.pageNumber = val
|
||||
this.pageData()
|
||||
},
|
||||
pageSizeChange(val) {
|
||||
this.pageForm.pageSize = val
|
||||
this.pageData()
|
||||
},
|
||||
pageData() {
|
||||
this.pageForm.unitId = this.currentData.id
|
||||
this.$axios.post(loc() + "/userPageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,472 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="所属单位">
|
||||
<el-select clearable filterable placeholder="请选择所属单位" style="width: 100%" v-model="pageForm.searchUnit" @change="doSearch">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id" v-for="item in units"></el-option>
|
||||
</el-select>
|
||||
<!-- <el-cascader :props="props" v-model="parentUnitSearch" placeholder="请选择单位" clearable filterable></el-cascader>-->
|
||||
</search-item>
|
||||
<search-item>
|
||||
<el-input placeholder="请输入内容" v-model="pageForm.searchKeyword" @keyup.enter.native="doSearch">
|
||||
<el-select v-model="pageForm.searchName" slot="prepend" placeholder="查询类型" style="width: 120px">
|
||||
<el-option label="工号" value="loginname"></el-option>
|
||||
<el-option label="姓名" value="username"></el-option>
|
||||
</el-select>
|
||||
<el-button slot="append" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
</el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never">
|
||||
<table-tool>
|
||||
<el-button size="small" @click="openAdd" type="primary">
|
||||
<i class="ti-plus"></i>
|
||||
新建用户
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table
|
||||
:key="tableKey"
|
||||
:data="tableData"
|
||||
@sort-change="pageOrder"
|
||||
header-align="center"
|
||||
:default-sort="{prop: 'createdAt', order: 'descending'}"
|
||||
style="width: 100%"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="35"></el-table-column>
|
||||
<el-table-column sortable prop="loginname" label="工号" header-align="center"></el-table-column>
|
||||
<el-table-column prop="username" label="姓名" header-align="center" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="sex" label="性别" header-align="center" :show-overflow-tooltip="true"></el-table-column>
|
||||
<el-table-column prop="unitName" label="所属单位" header-align="center" align="center" :show-overflow-tooltip="true"></el-table-column>
|
||||
<!-- <el-table-column sortable prop="disabled" label="帐号状态" header-align="center" align="center" :show-overflow-tooltip="true">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <i v-if="scope.row.disabled" class="fa fa-circle text-danger ml5"></i>-->
|
||||
<!-- <i v-if="!scope.row.disabled" class="fa fa-circle text-success ml5"></i>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<!-- <el-table-column sortable prop="userOnline" label="在线状态" header-align="center" align="center" :show-overflow-tooltip="true">-->
|
||||
<!-- <template slot-scope="scope">-->
|
||||
<!-- <i v-if="scope.row.userOnline" class="text-success ml5">在线</i>-->
|
||||
<!-- <i v-if="!scope.row.userOnline" class="text-danger ml5">离线</i>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
<el-table-column prop="userOnline" label="操作" width="120">
|
||||
<template slot-scope="scope">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'role',id:scope.row.id,loginname:scope.row.loginname}">查看权限</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'reset',id:scope.row.id,loginname:scope.row.loginname}">重置密码</el-dropdown-item>
|
||||
<el-dropdown-item divided :command="{type:'enable',id:scope.row.id,row:scope.row}">启用</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'disable',id:scope.row.id,row:scope.row}">禁用</el-dropdown-item>
|
||||
<el-dropdown-item divided :command="{type:'edit',id:scope.row.id}">修改</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:command="{type:'delete',id:scope.row.id,loginname:scope.row.loginname}"
|
||||
v-if="scope.row.loginname!='superadmin'"
|
||||
>
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="新建用户" :visible.sync="addDialogVisible" :close-on-click-modal="false" width="40%">
|
||||
<el-form :model="formData" ref="addForm" :rules="formRules" size="small" label-width="80px">
|
||||
<el-form-item class="is-required" prop="parentUnit" label="所属单位" label-width="80px">
|
||||
<el-cascader :props="props" v-model="parentUnit" placeholder="请选择单位" filterable style="width: 100%"></el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item prop="loginname" label="工号">
|
||||
<el-input maxlength="100" placeholder="工号" v-model="formData.loginname" auto-complete="off" tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username" label="姓名">
|
||||
<el-input maxlength="20" placeholder="姓名" v-model="formData.username" auto-complete="off" tabindex="3" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password" label="登录密码">
|
||||
<el-input
|
||||
maxlength="20"
|
||||
placeholder="登录密码"
|
||||
v-model="formData.password"
|
||||
auto-complete="off"
|
||||
tabindex="4"
|
||||
type="password"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="passwordAgain" label="确认密码">
|
||||
<el-input
|
||||
maxlength="20"
|
||||
placeholder="再输一次密码"
|
||||
v-model="formData.passwordAgain"
|
||||
auto-complete="off"
|
||||
tabindex="5"
|
||||
type="password"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="mobile" label="手机号码">
|
||||
<el-input maxlength="20" placeholder="手机号码" v-model="formData.mobile" auto-complete="off" tabindex="6" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doAdd">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="编辑用户" :visible.sync="editDialogVisible" :close-on-click-modal="false" width="40%">
|
||||
<el-form :model="formData" ref="editForm" :rules="editRules" size="small" label-width="80px">
|
||||
<el-form-item prop="loginname" label="工号">
|
||||
<el-input maxlength="100" placeholder="工号" v-model="formData.loginname" disabled tabindex="2" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="username" label="姓名">
|
||||
<el-input maxlength="20" placeholder="姓名" v-model="formData.username" auto-complete="off" tabindex="3" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="mobile" label="手机号码">
|
||||
<el-input maxlength="20" placeholder="手机号码" v-model="formData.mobile" auto-complete="off" tabindex="6" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="disabled" label="启用状态">
|
||||
<el-switch v-model="formData.disabled" active-color="#ff4949" inactive-color="#13ce66"></el-switch>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="editDialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doEdit">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog :title="roleDialogTitle" :visible.sync="roleDialogVisible" width="70%">
|
||||
<el-table :data="roleTableData" style="width: 100%" size="small" :highlight-current-row="true" row-key="id" lazy :load="loadRoleChild">
|
||||
<el-table-column
|
||||
label="菜单名称"
|
||||
header-align="center"
|
||||
prop="name"
|
||||
width="200"
|
||||
:show-overflow-tooltip="true"
|
||||
align="left"
|
||||
></el-table-column>
|
||||
|
||||
<el-table-column label="URL" header-align="center" prop="href" :show-overflow-tooltip="true"></el-table-column>
|
||||
|
||||
<el-table-column label="资源类型" header-align="center" prop="type" :show-overflow-tooltip="true" align="center">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="scope.row.type=='menu'" style="font-weight: bold">菜单</i>
|
||||
<i v-if="scope.row.type=='data'">数据</i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="权限标识" header-align="center" prop="permission" :show-overflow-tooltip="true"></el-table-column>
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="roleDialogVisible = false">关 闭</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
const self = this
|
||||
const validateLoginname = function (rule, value, callback) {
|
||||
const regex = new RegExp("^[a-zA-Z0-9][a-zA-Z0-9]{1,19}$")
|
||||
if (!regex.test(value)) {
|
||||
callback(new Error("工号以字母开头2-20字,允许使用字母/数字"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validatePass = function (rule, value, callback) {
|
||||
if (value !== self.formData.password) {
|
||||
callback(new Error("两次输入密码不一致!"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
const validateUnit = function (rule, value, callback) {
|
||||
if (
|
||||
self.parentUnit.length < 1 ||
|
||||
self.parentUnit[self.parentUnit.length - 1] === "" ||
|
||||
self.parentUnit[self.parentUnit.length - 1] === "root"
|
||||
) {
|
||||
callback(new Error("请选择所属单位!"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
addDialogVisible: false,
|
||||
editDialogVisible: false,
|
||||
roleDialogVisible: false,
|
||||
treeData: {
|
||||
label: "name",
|
||||
children: "zones",
|
||||
id: "id",
|
||||
isLeaf: "leaf"
|
||||
},
|
||||
tableData: [],
|
||||
tableKey: "",
|
||||
options: [],
|
||||
parentUnit: [],
|
||||
parentUnitSearch: [],
|
||||
pageForm: {
|
||||
searchUnit: "",
|
||||
searchName: "",
|
||||
searchKeyword: "",
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
formData: {
|
||||
id: "",
|
||||
unitId: "",
|
||||
disabled: false
|
||||
},
|
||||
formRules: {
|
||||
parentUnit: [{ validator: validateUnit, trigger: ["blur", "change"] }],
|
||||
loginname: [
|
||||
{ required: true, message: "工号", trigger: ["blur", "change"] },
|
||||
{ validator: validateLoginname, trigger: ["blur", "change"] }
|
||||
],
|
||||
username: [{ required: true, message: "姓名", trigger: ["blur", "change"] }],
|
||||
email: [
|
||||
{ required: false, message: "邮箱地址", trigger: ["blur", "change"] },
|
||||
{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }
|
||||
],
|
||||
mobile: [
|
||||
{ required: false, message: "手机号码", trigger: ["blur", "change"] },
|
||||
{ pattern: /^1[3456789]\d{9}$/, message: "请输入正确的手机号码" }
|
||||
],
|
||||
password: [
|
||||
{ required: true, message: "请输入密码", trigger: "blur" },
|
||||
{
|
||||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_=+{}[\]|;:'",.<>/?]).{8,20}$/,
|
||||
message: "密码必须同时包含大小写字母、数字、特殊符号,且长度在8到20位之间",
|
||||
trigger: "change"
|
||||
},
|
||||
{ min: 8, max: 20, message: "密码长度必须是8-20位", trigger: "change" }
|
||||
],
|
||||
passwordAgain: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{
|
||||
pattern: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[!@#$%^&*()\-_=+{}[\]|;:'",.<>/?]).{8,20}$/,
|
||||
message: "密码必须同时包含大小写字母、数字、特殊符号,且长度在8到20位之间",
|
||||
trigger: "change"
|
||||
},
|
||||
{ validator: validatePass, trigger: "change" }
|
||||
]
|
||||
},
|
||||
editRules: {
|
||||
username: [{ required: true, message: "姓名", trigger: ["blur", "change"] }],
|
||||
email: [
|
||||
{ required: false, message: "邮箱地址", trigger: ["blur", "change"] },
|
||||
{ type: "email", message: "请输入正确的邮箱地址", trigger: ["blur", "change"] }
|
||||
],
|
||||
mobile: [
|
||||
{ required: false, message: "手机号码", trigger: ["blur", "change"] },
|
||||
{ pattern: /^1[3456789]\d{9}$/, message: "请输入正确的手机号码" }
|
||||
]
|
||||
},
|
||||
roleDialogTitle: "查看用户权限",
|
||||
roleTableData: [], //treeTable后台取出的数据
|
||||
userId: "", //当前用户ID
|
||||
multipleSelection: [],
|
||||
props: {
|
||||
lazy: true,
|
||||
checkStrictly: true,
|
||||
multiple: false,
|
||||
value: "id",
|
||||
label: "name",
|
||||
lazyLoad: function (node, resolve) {
|
||||
const url = base + "/platform/sys/unit/tree"
|
||||
self.$axios.post(url, { pid: node.value }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
let filteredData = res.data;
|
||||
if (node.level === 0 && res.data && res.data.length > 0) {
|
||||
filteredData = res.data.slice(1); // 移除第一个元素
|
||||
}
|
||||
resolve(filteredData);
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
units: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatAt: function (val) {
|
||||
if (val > 0) return moment(val).format("YYYY-MM-DD HH:mm")
|
||||
return ""
|
||||
},
|
||||
doSearch: function () {
|
||||
// let unit = this.parentUnitSearch[this.parentUnitSearch.length - 1]
|
||||
// if ("root" === unit) {
|
||||
// unit = ""
|
||||
// }
|
||||
// this.pageForm.searchUnit = unit
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
openAdd() {
|
||||
this.addDialogVisible = true
|
||||
this.formData = {} //打开新增窗口,表单先清空
|
||||
if (this.$refs["addForm"]) this.$refs["addForm"].resetFields()
|
||||
},
|
||||
doAdd() {
|
||||
this.$refs["addForm"].validate((valid) => {
|
||||
if (valid) {
|
||||
this.formData.unitId = this.parentUnit[this.parentUnit.length - 1] || ""
|
||||
this.$axios.post("/platform/sys/user/addDo", this.formData).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.parentUnitSearch = []
|
||||
this.doSearch()
|
||||
this.addDialogVisible = false
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
doEdit() {
|
||||
this.$refs["editForm"].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$axios.post("/platform/sys/user/editDo", this.formData).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
this.editDialogVisible = false
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
initRoleTreeTable() {
|
||||
this.$axios.post("/platform/sys/user/menu/" + self.userId, { pid: "" }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
self.roleTableData = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
loadRoleChild(tree, treeNode, resolve) {
|
||||
this.$axios.post("/platform/sys/user/menu/" + this.userId, { pid: tree.id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
resolve(res.data)
|
||||
}
|
||||
})
|
||||
},
|
||||
dropdownCommand(command) {
|
||||
//监听下拉框事件
|
||||
if ("role" === command.type) {
|
||||
this.roleDialogVisible = true
|
||||
this.roleDialogTitle = "查看用户 " + command.loginname + " 的权限"
|
||||
this.userId = command.id
|
||||
this.initRoleTreeTable()
|
||||
}
|
||||
if ("reset" === command.type) {
|
||||
this.$confirm("确定重置用户" + command.loginname + "的密码", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
this.$axios.post("/platform/sys/user/resetPwd/" + command.id, {}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$alert("新密码为 " + res.data, "新密码", {
|
||||
confirmButtonText: "确定"
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
if ("enable" === command.type || "disable" === command.type) {
|
||||
this.$axios.post("/platform/sys/user/" + command.type + "/" + command.id, {}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
if ("disable" === command.type) {
|
||||
command.row.disabled = true
|
||||
}
|
||||
if ("enable" === command.type) {
|
||||
command.row.disabled = false
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if ("edit" === command.type) {
|
||||
this.$axios.post(base + "/platform/sys/user/edit/" + command.id, {}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.formData = res.data //加载后台表单数据
|
||||
this.editDialogVisible = true //打开编辑窗口
|
||||
}
|
||||
})
|
||||
}
|
||||
if ("delete" === command.type) {
|
||||
this.$confirm("此操作将删除 " + command.loginname, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/sys/user/delete/" + command.id, {}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
batchCommand(command) {
|
||||
if ("delete" === command.type) {
|
||||
if (this.multipleSelection.length === 0) {
|
||||
this.$message.warning("请选择要删除的用户")
|
||||
return
|
||||
}
|
||||
const loginnames = []
|
||||
const ids = []
|
||||
this.multipleSelection.forEach(function (val) {
|
||||
loginnames.push(val.loginname)
|
||||
ids.push(val.id)
|
||||
})
|
||||
this.$confirm("此操作将删除用户 " + loginnames.toString(), "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/sys/user/delete", { ids: ids.toString() }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
if ("export" === command.type) {
|
||||
this.$downLoad.post("/platform/sys/user/export", this.pageForm)
|
||||
}
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.pageData()
|
||||
this.units = await this.$businessTool.listUnit()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,116 @@
|
||||
<div id="passApp">
|
||||
<div class="el-dialog__header">
|
||||
<button type="button" data-dismiss="modal" aria-hidden="true" aria-label="Close" class="el-dialog__headerbtn">
|
||||
<i class="el-dialog__close el-icon el-icon-close"></i>
|
||||
</button>
|
||||
<h4 class="modal-title">${msg['index.user.password']}</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<el-form :model="passForm" ref="passForm" :rules="passRules" label-width="100px">
|
||||
<el-form-item label="${msg['index.user.oldpass']}" prop="oldPassword">
|
||||
<el-input
|
||||
size="small"
|
||||
maxlength="20"
|
||||
placeholder="${msg['index.user.oldpass']}"
|
||||
v-model="passForm.oldPassword"
|
||||
auto-complete="off"
|
||||
tabindex="1"
|
||||
type="password"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="输入新密码" prop="newPassword">
|
||||
<el-input
|
||||
size="small"
|
||||
maxlength="20"
|
||||
placeholder="${msg['index.user.newpass']}"
|
||||
v-model="passForm.newPassword"
|
||||
auto-complete="off"
|
||||
tabindex="2"
|
||||
type="password"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="确认新密码" prop="passwordAgain">
|
||||
<el-input
|
||||
size="small"
|
||||
maxlength="20"
|
||||
placeholder="${msg['index.user.rightpass']}"
|
||||
v-model="passForm.passwordAgain"
|
||||
auto-complete="off"
|
||||
tabindex="3"
|
||||
type="password"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div class="el-dialog__footer">
|
||||
<span class="dialog-footer">
|
||||
<button type="button" class="el-button el-button--default" data-dismiss="modal">
|
||||
<span>${msg['system.cancel']}</span>
|
||||
</button>
|
||||
<button type="button" class="el-button el-button--primary" @click="doPass">
|
||||
<span>${msg['system.confirm']}</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}" type="text/javascript">
|
||||
new Vue({
|
||||
el: "#passApp",
|
||||
data: function () {
|
||||
var self = this
|
||||
var validatePass = function (rule, value, callback) {
|
||||
if (value !== self.passForm.newPassword) {
|
||||
callback(new Error("两次输入密码不一致!"))
|
||||
} else {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
return {
|
||||
passForm: {
|
||||
oldPassword: "",
|
||||
newPassword: "",
|
||||
passwordAgain: ""
|
||||
},
|
||||
passRules: {
|
||||
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
|
||||
newPassword: [
|
||||
{ required: true, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 3, max: 20, message: "密码长度必须是3-20位", trigger: "change" }
|
||||
],
|
||||
passwordAgain: [
|
||||
{ required: true, message: "请再次输入新密码", trigger: "blur" },
|
||||
{ validator: validatePass, trigger: "change" }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doPass: function () {
|
||||
var self = this
|
||||
self.$refs["passForm"].validate(function (valid) {
|
||||
if (valid) {
|
||||
$.post(
|
||||
base + "/platform/sys/user/doChangePassword",
|
||||
self.passForm,
|
||||
function (data) {
|
||||
if (data.code == 0) {
|
||||
$("#homeDetail").modal("hide")
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "success"
|
||||
})
|
||||
} else {
|
||||
self.$message({
|
||||
message: data.msg,
|
||||
type: "error"
|
||||
})
|
||||
}
|
||||
},
|
||||
"json"
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,224 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="名称" prop="name">
|
||||
<el-input v-model="pageForm.name" clearable placeholder="请输入名称"></el-input>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card shadow="never">
|
||||
<el-table :data="tableData" header-align="center" border style="width: 100%">
|
||||
<el-table-column label="序号" type="index" width="50px" :index="indexMethod"></el-table-column>
|
||||
<el-table-column label="模板名称" prop="templateName" width="260px">
|
||||
<template slot-scope="scope">
|
||||
<el-input
|
||||
v-model="scope.row.templateName"
|
||||
size="mini"
|
||||
clearable
|
||||
placeholder="请输入模板名称"
|
||||
@change="updateTemplateName(scope.row)">
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sortNo" width="120px" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-input-number
|
||||
v-model="scope.row.sortNo"
|
||||
size="mini"
|
||||
:min="0"
|
||||
:controls="false"
|
||||
style="width: 80px"
|
||||
@change="updateSortNo(scope.row)">
|
||||
</el-input-number>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" prop="name"></el-table-column>
|
||||
<el-table-column label="PC端链接" prop="url" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="模板图标" prop="templateIcon" width="120px" align="center">
|
||||
<template slot-scope="scope">
|
||||
<file-upload
|
||||
class="template-icon-upload"
|
||||
:upload_number="1"
|
||||
:value="scope.row.templateIcon"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
upload_result_type="url"
|
||||
upload_result_category="interval"
|
||||
upload_mode="image"
|
||||
@update:value="onTemplateIconChange(scope.row, $event)">
|
||||
</file-upload>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="模板文件" prop="templateFile" width="170px" align="center">
|
||||
<template slot-scope="scope">
|
||||
<file-upload
|
||||
class="template-file-upload"
|
||||
:upload_number="1"
|
||||
:value="scope.row.templateFile"
|
||||
accept=".doc,.docx,.xls,.xlsx,.pdf,.ppt,.pptx,.jpg,.jpeg,.png"
|
||||
upload_result_type="url"
|
||||
upload_result_category="interval"
|
||||
upload_mode="file"
|
||||
@update:value="onTemplateFileChange(scope.row, $event)">
|
||||
</file-upload>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="enable" width="80px">
|
||||
<template slot-scope="scope">
|
||||
<i v-if="!scope.row.enable" class="fa fa-circle text-danger ml5"></i>
|
||||
<i v-else class="fa fa-circle text-success ml5"></i>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="180px">
|
||||
<template slot-scope="scope">
|
||||
<el-link type="danger" size="mini" @click="disable(scope.row.id)" v-if="scope.row.enable">关闭</el-link>
|
||||
<el-link type="primary" size="mini" @click="enable(scope.row.id)" v-if="!scope.row.enable">开启</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {
|
||||
disable(id) {
|
||||
this.$confirm("您确定要关闭吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/disable", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
enable(id) {
|
||||
this.$confirm("您确定要开启吗?", "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/enable", {id}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
updateTemplateName(row) {
|
||||
this.$axios.post(loc() + "/updateTemplateName", {
|
||||
id: row.id,
|
||||
templateName: row.templateName
|
||||
}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
updateSortNo(row) {
|
||||
this.$axios.post(loc() + "/updateSortNo", {
|
||||
id: row.id,
|
||||
sortNo: row.sortNo
|
||||
}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
onTemplateIconChange(row, templateIcon) {
|
||||
if (templateIcon === undefined && !row.templateIcon) {
|
||||
return
|
||||
}
|
||||
row.templateIcon = templateIcon
|
||||
this.updateTemplateIcon(row)
|
||||
},
|
||||
updateTemplateIcon(row) {
|
||||
this.$nextTick(() => {
|
||||
this.$axios.post(loc() + "/updateTemplateIcon", {
|
||||
id: row.id,
|
||||
templateIcon: row.templateIcon
|
||||
}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
onTemplateFileChange(row, templateFile) {
|
||||
if (templateFile === undefined && !row.templateFile) {
|
||||
return
|
||||
}
|
||||
row.templateFile = templateFile
|
||||
this.updateTemplateFile(row)
|
||||
},
|
||||
updateTemplateFile(row) {
|
||||
this.$nextTick(() => {
|
||||
this.$axios.post(loc() + "/updateTemplateFile", {
|
||||
id: row.id,
|
||||
templateFile: row.templateFile
|
||||
}).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
.template-icon-upload,
|
||||
.template-file-upload {
|
||||
--upload-width: 48px;
|
||||
--upload-height: 48px;
|
||||
}
|
||||
|
||||
.template-icon-upload .el-upload-list--picture-card .el-upload-list__item,
|
||||
.template-icon-upload .el-upload--picture-card,
|
||||
.template-file-upload .el-upload-list--picture-card .el-upload-list__item,
|
||||
.template-file-upload .el-upload--picture-card {
|
||||
width: 48px !important;
|
||||
height: 48px !important;
|
||||
line-height: 48px !important;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.template-icon-upload .el-upload--picture-card i,
|
||||
.template-file-upload .el-upload--picture-card i {
|
||||
font-size: 18px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,9 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">学习活动管理模块待完善</el-card>
|
||||
</div>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,884 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.learning-outline-layout {
|
||||
height: calc(100vh - 235px);
|
||||
}
|
||||
.learning-outline-tree {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
.outline-context-menu {
|
||||
position: fixed;
|
||||
z-index: 3000;
|
||||
padding: 5px 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1);
|
||||
}
|
||||
.outline-context-menu li {
|
||||
min-width: 120px;
|
||||
padding: 7px 18px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.outline-context-menu li:hover {
|
||||
background: #f2f6fc;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="课程名称">
|
||||
<el-select
|
||||
v-model="currentCourseId"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="courseNameRemoteSearch"
|
||||
placeholder="请选择或搜索课程名称"
|
||||
style="width: 100%"
|
||||
@change="courseChange"
|
||||
@clear="courseClear">
|
||||
<el-option v-for="item in courseOptions" :key="item.id" :label="item.courseName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="课程类型">
|
||||
<el-select v-model="courseQuery.courseTypeId" clearable filterable placeholder="请选择课程类型" style="width: 100%">
|
||||
<el-option v-for="item in courseTypeOptions" :key="item.id" :label="item.typeName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="授课讲师">
|
||||
<el-input v-model="courseQuery.lecturerName" clearable placeholder="请输入授课讲师" style="width: 100%" @keyup.enter.native="loadCourses"></el-input>
|
||||
</search-item>
|
||||
<search-item label="课程状态">
|
||||
<el-select v-model="courseQuery.status" clearable placeholder="请选择课程状态" style="width: 100%">
|
||||
<el-option v-for="item in statusOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="loadCourses">搜索</el-button>
|
||||
<el-button size="medium" @click="resetCourseQuery">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-row :gutter="10" class="mt10 learning-outline-layout">
|
||||
<el-col :span="6" style="height: 100%">
|
||||
<el-card shadow="never" style="height: 100%">
|
||||
<table-tool label="课程大纲">
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openNodeForm('chapter')">新增章</el-button>
|
||||
</table-tool>
|
||||
<div class="learning-outline-tree">
|
||||
<el-tree
|
||||
ref="outlineTree"
|
||||
:data="outlineTreeData"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
:expand-on-click-node="false"
|
||||
:props="{children:'children', label:'title'}"
|
||||
@node-click="nodeClick"
|
||||
@node-contextmenu="openContextMenu">
|
||||
<span slot-scope="{ node, data }">
|
||||
<i :class="data.nodeType === 'course' ? 'el-icon-collection' : (data.nodeType === 'chapter' ? 'el-icon-folder' : 'el-icon-document')"></i>
|
||||
<span>{{data.title}}</span>
|
||||
<el-tag v-if="data.status === 'disabled'" size="mini" type="info" style="margin-left: 6px">禁用</el-tag>
|
||||
<el-tag v-if="data.required" size="mini" type="warning" style="margin-left: 6px">必学</el-tag>
|
||||
</span>
|
||||
</el-tree>
|
||||
<el-empty v-if="treeData.length === 0" description="暂无课程大纲"></el-empty>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="18" style="height: 100%">
|
||||
<el-card shadow="never" style="height: 100%; overflow: auto">
|
||||
<el-empty v-if="!selectedNode.id" description="请选择左侧章/节节点"></el-empty>
|
||||
<el-tabs v-else v-model="activeTab">
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-form :model="nodeForm" ref="nodeForm" label-width="110px" :rules="nodeRules">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="节点类型">
|
||||
<el-tag :type="nodeForm.nodeType === 'chapter' ? 'primary' : 'success'">{{nodeForm.nodeType === 'chapter' ? '章' : '节'}}</el-tag>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序值" prop="sortOrder">
|
||||
<el-input-number v-model="nodeForm.sortOrder" :controls="false" :min="1" :precision="0" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="nodeForm.title" maxlength="100" placeholder="请输入章/节标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="副标题" prop="subtitle">
|
||||
<el-input v-model="nodeForm.subtitle" maxlength="200" placeholder="请输入副标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介" prop="description">
|
||||
<el-input v-model="nodeForm.description" type="textarea" :rows="5" maxlength="2000" show-word-limit placeholder="请输入章/节简介"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveNodeBasic">保存基础信息</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="学习资料" name="resource">
|
||||
<table-tool label="学习资料">
|
||||
<el-button size="mini" type="primary" icon="el-icon-plus" @click="openResourceForm()">新增资料</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="resourceData" border @sort-change="resourcePageOrder">
|
||||
<el-table-column label="序号" type="index" width="70" :index="resourceIndex"></el-table-column>
|
||||
<el-table-column label="资料标题" prop="resourceTitle" sortable="custom" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="资料类型" prop="resourceType" sortable="custom" width="110">
|
||||
<template slot-scope="{row}">{{getResourceTypeName(row.resourceType)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="文件格式" prop="fileExt" sortable="custom" width="110"></el-table-column>
|
||||
<el-table-column label="学习时长(秒)" prop="durationSeconds" sortable="custom" width="140">
|
||||
<template slot-scope="{row}">{{formatDurationSeconds(row.durationSeconds)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="排序" prop="sortOrder" sortable="custom" width="90"></el-table-column>
|
||||
<el-table-column label="必学" prop="required" sortable="custom" width="90">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="row.required ? 'warning' : 'info'" size="mini">{{row.required ? '是' : '否'}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="预览" prop="allowPreview" sortable="custom" width="90">
|
||||
<template slot-scope="{row}">{{row.allowPreview ? '允许' : '不允许'}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="下载" prop="allowDownload" sortable="custom" width="90">
|
||||
<template slot-scope="{row}">{{row.allowDownload ? '允许' : '不允许'}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" prop="status" sortable="custom" width="90">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="row.status === 'enabled' ? 'success' : 'info'" size="mini">{{row.status === 'enabled' ? '启用' : '禁用'}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="primary" @click="openResourceForm(row)">编辑</el-button>
|
||||
<el-button size="mini" type="danger" @click="deleteResource(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
class="mt20"
|
||||
@size-change="resourceSizeChange"
|
||||
@current-change="resourceNumberChange"
|
||||
:current-page="resourcePageForm.pageNumber"
|
||||
:page-sizes="[5,10,20,30,50]"
|
||||
:page-size="resourcePageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="resourcePageForm.totalCount">
|
||||
</el-pagination>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="完成规则" name="rule">
|
||||
<el-form :model="ruleForm" label-width="150px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否必学">
|
||||
<el-switch v-model="ruleForm.required"></el-switch>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学习方式">
|
||||
<el-select v-model="ruleForm.studyMode" style="width: 100%">
|
||||
<el-option v-for="item in studyModeOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="完成规则">
|
||||
<el-select v-model="ruleForm.completionRule" style="width: 100%">
|
||||
<el-option v-for="item in completionRuleOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="完成比例">
|
||||
<el-input-number v-model="ruleForm.completePercent" :min="0" :max="100" :precision="0" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="最少学习时长(分钟)">
|
||||
<el-input-number v-model="ruleForm.minStudyMinutes" :min="0" :precision="0" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="解锁规则">
|
||||
<el-select v-model="ruleForm.unlockRule" style="width: 100%">
|
||||
<el-option v-for="item in unlockRuleOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"><el-form-item label="允许跳过"><el-switch v-model="ruleForm.allowSkip"></el-switch></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="允许拖动"><el-switch v-model="ruleForm.allowDrag"></el-switch></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="暂停计时"><el-switch v-model="ruleForm.pauseCountTime"></el-switch></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"><el-form-item label="隐藏计时"><el-switch v-model="ruleForm.hiddenCountTime"></el-switch></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="无操作计时"><el-switch v-model="ruleForm.inactiveCountTime"></el-switch></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveRule">保存完成规则</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="状态设置" name="status">
|
||||
<el-form :model="nodeForm" label-width="110px">
|
||||
<el-form-item label="节点状态">
|
||||
<el-radio-group v-model="nodeForm.status">
|
||||
<el-radio label="enabled">启用</el-radio>
|
||||
<el-radio label="disabled">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否必学">
|
||||
<el-switch v-model="nodeForm.required"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="saveNodeBasic">保存状态</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<ul v-show="contextVisible" class="outline-context-menu" :style="{left: contextLeft + 'px', top: contextTop + 'px'}">
|
||||
<li @click="openNodeForm('chapter')">新增章</li>
|
||||
<li v-if="contextNode.nodeType === 'chapter'" @click="openNodeForm('section', contextNode)">新增节</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="openNodeForm(contextNode.nodeType, contextNode)">编辑</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="deleteNode(contextNode)">删除</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="toggleNode(contextNode)">{{contextNode.status === 'enabled' ? '禁用' : '启用'}}</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="moveNode(contextNode, 'up')">上移</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="moveNode(contextNode, 'down')">下移</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="openResourceForm(null, contextNode)">上传资料</li>
|
||||
<li v-if="contextNode.nodeType !== 'course'" @click="setRequired(contextNode)">设置必学</li>
|
||||
</ul>
|
||||
|
||||
<el-dialog :title="nodeDialogTitle" :visible.sync="nodeDialogVisible" :close-on-click-modal="false" width="45%">
|
||||
<el-form :model="nodeDialogForm" ref="nodeDialogForm" label-width="100px" :rules="nodeRules">
|
||||
<el-form-item label="节点类型">
|
||||
<el-tag>{{nodeDialogForm.nodeType === 'chapter' ? '章' : '节'}}</el-tag>
|
||||
</el-form-item>
|
||||
<el-form-item label="上级节点">
|
||||
<el-input v-model="nodeDialogForm.parentName" disabled placeholder="上级节点"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="nodeDialogForm.title" maxlength="100" placeholder="请输入标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="副标题">
|
||||
<el-input v-model="nodeDialogForm.subtitle" maxlength="200" placeholder="请输入副标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="简介">
|
||||
<el-input v-model="nodeDialogForm.description" type="textarea" :rows="4" maxlength="2000" show-word-limit></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序值">
|
||||
<el-input-number v-model="nodeDialogForm.sortOrder" :min="1" :precision="0" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否必学">
|
||||
<el-switch v-model="nodeDialogForm.required"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="nodeDialogForm.status">
|
||||
<el-radio label="enabled">启用</el-radio>
|
||||
<el-radio label="disabled">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="nodeDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitNodeDialog">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="resourceForm.id ? '编辑学习资料' : '新增学习资料'" :visible.sync="resourceDialogVisible" :close-on-click-modal="false" width="55%">
|
||||
<el-form :model="resourceForm" ref="resourceForm" label-width="120px" :rules="resourceRules">
|
||||
<el-form-item label="所属章节">
|
||||
<el-input v-model="resourceForm.outlineName" disabled placeholder="所属章节"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="资料标题" prop="resourceTitle">
|
||||
<el-input v-model="resourceForm.resourceTitle" maxlength="100" placeholder="请输入资料标题"></el-input>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资料类型" prop="resourceType">
|
||||
<el-select v-model="resourceForm.resourceType" placeholder="请选择资料类型" style="width: 100%" @change="resourceTypeChange">
|
||||
<el-option v-for="item in resourceTypeOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序值" prop="sortOrder">
|
||||
<el-input-number v-model="resourceForm.sortOrder" :min="1" :precision="0" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学习时长(秒)">
|
||||
<el-input-number v-model="resourceForm.durationSeconds" :min="0" :precision="0" style="width: 100%"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="状态">
|
||||
<el-radio-group v-model="resourceForm.status">
|
||||
<el-radio label="enabled">启用</el-radio>
|
||||
<el-radio label="disabled">禁用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8"><el-form-item label="是否必学"><el-switch v-model="resourceForm.required"></el-switch></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="允许预览"><el-switch v-model="resourceForm.allowPreview"></el-switch></el-form-item></el-col>
|
||||
<el-col :span="8"><el-form-item label="允许下载"><el-switch v-model="resourceForm.allowDownload"></el-switch></el-form-item></el-col>
|
||||
</el-row>
|
||||
<el-form-item label="附件" prop="fileData">
|
||||
<file-upload
|
||||
:value.sync="resourceForm.fileData"
|
||||
:upload_number="1"
|
||||
upload_mode="drag"
|
||||
upload_result_category="array"
|
||||
complete_result
|
||||
:accept="fileAccept">
|
||||
</file-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="resourceDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submitResource">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
courseQuery: {},
|
||||
courseOptions: [],
|
||||
courseTypeOptions: [],
|
||||
currentCourseId: "",
|
||||
treeData: [],
|
||||
selectedNode: {},
|
||||
activeTab: "basic",
|
||||
nodeForm: {},
|
||||
nodeDialogVisible: false,
|
||||
nodeDialogTitle: "",
|
||||
nodeDialogForm: {},
|
||||
nodeRules: {
|
||||
title: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
sortOrder: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
},
|
||||
contextVisible: false,
|
||||
contextLeft: 0,
|
||||
contextTop: 0,
|
||||
contextNode: {},
|
||||
resourceData: [],
|
||||
resourcePageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
pageOrderName: "",
|
||||
pageOrderBy: ""
|
||||
},
|
||||
resourceDialogVisible: false,
|
||||
resourceForm: {},
|
||||
resourceRules: {
|
||||
resourceTitle: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
resourceType: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
sortOrder: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
fileData: [{ required: true, message: "请上传附件", trigger: ["blur", "change"] }]
|
||||
},
|
||||
fileAccept: "",
|
||||
ruleForm: {},
|
||||
statusOptions: [
|
||||
{ name: "草稿", code: "draft" },
|
||||
{ name: "待发布", code: "pending" },
|
||||
{ name: "已发布", code: "published" },
|
||||
{ name: "已下架", code: "offline" }
|
||||
],
|
||||
resourceTypeOptions: [
|
||||
{ name: "视频", code: "video", accept: ".mp4,.mov" },
|
||||
{ name: "音频", code: "audio", accept: ".mp3,.wav,.aac" },
|
||||
{ name: "PDF", code: "pdf", accept: ".pdf" },
|
||||
{ name: "PPT课件", code: "ppt", accept: ".ppt,.pptx" },
|
||||
{ name: "Word文档", code: "word", accept: ".doc,.docx" },
|
||||
{ name: "图片", code: "image", accept: ".jpg,.jpeg,.png" },
|
||||
{ name: "其他", code: "other", accept: "" }
|
||||
],
|
||||
studyModeOptions: [
|
||||
{ name: "视频", code: "video" },
|
||||
{ name: "音频", code: "audio" },
|
||||
{ name: "文档", code: "document" },
|
||||
{ name: "图片", code: "image" },
|
||||
{ name: "混合资料", code: "mixed" }
|
||||
],
|
||||
completionRuleOptions: [
|
||||
{ name: "完成所有必学资料", code: "all_required_resource" },
|
||||
{ name: "按资料学习进度", code: "resource_progress" },
|
||||
{ name: "达到最少学习时长", code: "min_study_time" },
|
||||
{ name: "管理员手动确认", code: "manual" }
|
||||
],
|
||||
unlockRuleOptions: [
|
||||
{ name: "自由学习", code: "free" },
|
||||
{ name: "顺序解锁", code: "sequential" }
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
currentCourse() {
|
||||
return this.courseOptions.find(v => v.id === this.currentCourseId)
|
||||
},
|
||||
outlineTreeData() {
|
||||
if (!this.currentCourseId || !this.currentCourse) {
|
||||
return []
|
||||
}
|
||||
return [{
|
||||
id: "course-" + this.currentCourseId,
|
||||
courseId: this.currentCourseId,
|
||||
title: this.currentCourse.courseName,
|
||||
nodeType: "course",
|
||||
children: this.treeData
|
||||
}]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetCourseQuery() {
|
||||
this.courseQuery = {}
|
||||
this.currentCourseId = ""
|
||||
this.loadCourses()
|
||||
},
|
||||
getQueryParam(name) {
|
||||
return new URLSearchParams(window.location.search).get(name) || ""
|
||||
},
|
||||
async loadCourseTypes() {
|
||||
const resp = await this.$axios.post(loc() + "/courseTypes")
|
||||
if (resp.code === 0) {
|
||||
this.courseTypeOptions = resp.data
|
||||
}
|
||||
},
|
||||
async loadCourses(keepCurrent = false) {
|
||||
const resp = await this.$axios.post(loc() + "/courses", this.courseQuery)
|
||||
if (resp.code === 0) {
|
||||
this.courseOptions = resp.data
|
||||
if (!keepCurrent && (!this.currentCourseId || !this.courseOptions.some(v => v.id === this.currentCourseId))) {
|
||||
this.currentCourseId = this.courseOptions.length ? this.courseOptions[0].id : ""
|
||||
}
|
||||
await this.loadTree()
|
||||
}
|
||||
},
|
||||
courseNameRemoteSearch(query) {
|
||||
this.courseQuery.courseName = query
|
||||
this.loadCourses(true)
|
||||
},
|
||||
courseChange() {
|
||||
this.selectedNode = {}
|
||||
this.loadTree()
|
||||
},
|
||||
courseClear() {
|
||||
this.currentCourseId = ""
|
||||
this.selectedNode = {}
|
||||
this.treeData = []
|
||||
},
|
||||
async loadTree() {
|
||||
if (!this.currentCourseId) {
|
||||
this.treeData = []
|
||||
this.selectedNode = {}
|
||||
return
|
||||
}
|
||||
const resp = await this.$axios.post(loc() + "/tree", { courseId: this.currentCourseId })
|
||||
if (resp.code === 0) {
|
||||
this.treeData = resp.data
|
||||
let nodeToSelect = null
|
||||
if (this.selectedNode.id) {
|
||||
nodeToSelect = this.findNode(this.treeData, this.selectedNode.id)
|
||||
}
|
||||
if (!nodeToSelect) {
|
||||
nodeToSelect = this.findFirstOutlineNode(this.treeData)
|
||||
}
|
||||
if (nodeToSelect) {
|
||||
this.nodeClick(nodeToSelect)
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.outlineTree) {
|
||||
this.$refs.outlineTree.setCurrentKey(nodeToSelect.id)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.selectedNode = {}
|
||||
this.nodeForm = {}
|
||||
this.resourceData = []
|
||||
}
|
||||
}
|
||||
},
|
||||
findFirstOutlineNode(nodes) {
|
||||
if (!nodes || !nodes.length) return null
|
||||
for (const node of nodes) {
|
||||
if (node.nodeType !== "course") return node
|
||||
const child = this.findFirstOutlineNode(node.children || [])
|
||||
if (child) return child
|
||||
}
|
||||
return null
|
||||
},
|
||||
findNode(nodes, id) {
|
||||
for (const node of nodes) {
|
||||
if (node.id === id) return node
|
||||
const child = this.findNode(node.children || [], id)
|
||||
if (child) return child
|
||||
}
|
||||
return null
|
||||
},
|
||||
getParentName(type, node, course, isEdit) {
|
||||
if (type === "chapter") {
|
||||
return course ? course.courseName : ""
|
||||
}
|
||||
if (!isEdit) {
|
||||
return node ? node.title : ""
|
||||
}
|
||||
const parent = node && node.parentId ? this.findNode(this.treeData, node.parentId) : null
|
||||
return parent ? parent.title : (course ? course.courseName : "")
|
||||
},
|
||||
nodeClick(data) {
|
||||
if (data.nodeType === "course") {
|
||||
this.selectedNode = {}
|
||||
this.nodeForm = {}
|
||||
this.resourceData = []
|
||||
return
|
||||
}
|
||||
this.selectedNode = data
|
||||
this.nodeForm = Object.assign({}, data)
|
||||
this.activeTab = "basic"
|
||||
this.loadResources()
|
||||
this.loadRule()
|
||||
},
|
||||
openContextMenu(event, data) {
|
||||
event.preventDefault()
|
||||
this.contextNode = data
|
||||
this.contextVisible = true
|
||||
this.contextLeft = event.clientX
|
||||
this.contextTop = event.clientY
|
||||
},
|
||||
openNodeForm(type, node = null) {
|
||||
if (!this.currentCourseId) {
|
||||
this.$message.warning("请先选择课程")
|
||||
return
|
||||
}
|
||||
const course = this.courseOptions.find(v => v.id === this.currentCourseId)
|
||||
if (node && node.id && node.nodeType === type) {
|
||||
this.nodeDialogTitle = "编辑" + (type === "chapter" ? "章" : "节")
|
||||
this.nodeDialogForm = Object.assign({}, node, {
|
||||
parentName: this.getParentName(type, node, course, true)
|
||||
})
|
||||
} else {
|
||||
this.nodeDialogTitle = "新增" + (type === "chapter" ? "章" : "节")
|
||||
this.nodeDialogForm = {
|
||||
courseId: this.currentCourseId,
|
||||
courseName: course ? course.courseName : "",
|
||||
parentId: type === "section" && node ? node.id : "",
|
||||
parentName: this.getParentName(type, node, course, false),
|
||||
nodeType: type,
|
||||
title: "",
|
||||
subtitle: "",
|
||||
description: "",
|
||||
sortOrder: 1,
|
||||
required: false,
|
||||
status: "enabled"
|
||||
}
|
||||
}
|
||||
this.nodeDialogVisible = true
|
||||
this.$nextTick(() => this.$refs.nodeDialogForm && this.$refs.nodeDialogForm.clearValidate())
|
||||
},
|
||||
submitNodeDialog() {
|
||||
this.$refs.nodeDialogForm.validate(async valid => {
|
||||
if (!valid) return
|
||||
const resp = await this.$axios.post(loc() + "/saveNode", this.nodeDialogForm)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.nodeDialogVisible = false
|
||||
await this.loadTree()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
saveNodeBasic() {
|
||||
this.$refs.nodeForm.validate(async valid => {
|
||||
if (!valid) return
|
||||
const resp = await this.$axios.post(loc() + "/saveNode", this.nodeForm)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.selectedNode = Object.assign({}, this.nodeForm)
|
||||
await this.loadTree()
|
||||
await this.loadRule()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteNode(node) {
|
||||
this.$confirm("确定要删除该节点吗?若下级有内容将不允许删除。", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post(loc() + "/deleteNode", { id: node.id })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.selectedNode = {}
|
||||
await this.loadTree()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
async toggleNode(node) {
|
||||
const status = node.status === "enabled" ? "disabled" : "enabled"
|
||||
const resp = await this.$axios.post(loc() + "/toggleNode", { id: node.id, status })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
await this.loadTree()
|
||||
}
|
||||
},
|
||||
async moveNode(node, direction) {
|
||||
const resp = await this.$axios.post(loc() + "/moveNode", { id: node.id, direction })
|
||||
if (resp.code === 0) {
|
||||
await this.loadTree()
|
||||
}
|
||||
},
|
||||
async setRequired(node) {
|
||||
const next = !node.required
|
||||
const data = Object.assign({}, node, { required: next })
|
||||
const resp = await this.$axios.post(loc() + "/saveNode", data)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
await this.loadTree()
|
||||
if (this.selectedNode.id === node.id) {
|
||||
this.nodeClick(Object.assign({}, node, { required: next }))
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadResources() {
|
||||
if (!this.selectedNode.id) return
|
||||
const resp = await this.$axios.post(loc() + "/resourcePage", Object.assign({}, this.resourcePageForm, { outlineId: this.selectedNode.id }))
|
||||
if (resp.code === 0) {
|
||||
this.resourceData = resp.data.list
|
||||
this.resourcePageForm.totalCount = resp.data.totalCount
|
||||
}
|
||||
},
|
||||
resourcePageOrder(column) {
|
||||
this.resourcePageForm.pageOrderName = column.prop
|
||||
this.resourcePageForm.pageOrderBy = column.order
|
||||
this.loadResources()
|
||||
},
|
||||
resourceSizeChange(val) {
|
||||
this.resourcePageForm.pageSize = val
|
||||
this.loadResources()
|
||||
},
|
||||
resourceNumberChange(val) {
|
||||
this.resourcePageForm.pageNumber = val
|
||||
this.loadResources()
|
||||
},
|
||||
resourceIndex(index) {
|
||||
return index + (this.resourcePageForm.pageNumber - 1) * this.resourcePageForm.pageSize + 1
|
||||
},
|
||||
openResourceForm(row = null, node = null) {
|
||||
const target = node || this.selectedNode
|
||||
if (!target.id) {
|
||||
this.$message.warning("请先选择章/节节点")
|
||||
return
|
||||
}
|
||||
if (row) {
|
||||
this.resourceForm = Object.assign({}, row, {
|
||||
outlineName: target.title,
|
||||
fileData: this.parseFiles(row.fileData)
|
||||
})
|
||||
} else {
|
||||
this.resourceForm = {
|
||||
courseId: target.courseId,
|
||||
outlineId: target.id,
|
||||
outlineName: target.title,
|
||||
resourceTitle: "",
|
||||
resourceType: "video",
|
||||
fileExt: "",
|
||||
fileData: [],
|
||||
durationSeconds: 0,
|
||||
sortOrder: 1,
|
||||
required: false,
|
||||
allowPreview: true,
|
||||
allowDownload: false,
|
||||
status: "enabled"
|
||||
}
|
||||
}
|
||||
this.resourceTypeChange(this.resourceForm.resourceType)
|
||||
this.resourceDialogVisible = true
|
||||
this.$nextTick(() => this.$refs.resourceForm && this.$refs.resourceForm.clearValidate())
|
||||
},
|
||||
resourceTypeChange(val) {
|
||||
const type = this.resourceTypeOptions.find(v => v.code === val)
|
||||
this.fileAccept = type ? type.accept : ""
|
||||
this.autoFillMediaDuration()
|
||||
},
|
||||
submitResource() {
|
||||
this.$refs.resourceForm.validate(async valid => {
|
||||
if (!valid) return
|
||||
const data = Object.assign({}, this.resourceForm)
|
||||
data.fileExt = this.getFileExt(data.fileData)
|
||||
if (!this.validResourceExt(data.resourceType, data.fileExt)) {
|
||||
this.$message.warning("当前资料类型不支持上传 ." + data.fileExt + " 格式文件")
|
||||
return
|
||||
}
|
||||
delete data.outlineName
|
||||
data.fileData = JSON.stringify(data.fileData || [])
|
||||
const resp = await this.$axios.post(loc() + "/saveResource", data)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.resourceDialogVisible = false
|
||||
this.loadResources()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
deleteResource(row) {
|
||||
this.$confirm("确定要删除该学习资料吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post(loc() + "/deleteResource", { id: row.id })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.loadResources()
|
||||
}
|
||||
})
|
||||
},
|
||||
async loadRule() {
|
||||
if (!this.selectedNode.id) return
|
||||
const resp = await this.$axios.post(loc() + "/getRule", {
|
||||
courseId: this.selectedNode.courseId,
|
||||
targetType: "outline",
|
||||
targetId: this.selectedNode.id
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.ruleForm = Object.assign({}, resp.data, {
|
||||
minStudyMinutes: this.formatDurationMinutes(resp.data.minStudySeconds)
|
||||
})
|
||||
}
|
||||
},
|
||||
async saveRule() {
|
||||
const data = Object.assign({}, this.ruleForm, {
|
||||
minStudySeconds: Math.round((this.ruleForm.minStudyMinutes || 0) * 60)
|
||||
})
|
||||
delete data.minStudyMinutes
|
||||
const resp = await this.$axios.post(loc() + "/saveRule", data)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
await this.loadTree()
|
||||
await this.loadRule()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
parseFiles(value) {
|
||||
if (!value) return []
|
||||
if (Array.isArray(value)) return value
|
||||
try {
|
||||
return JSON.parse(value)
|
||||
} catch (e) {
|
||||
return []
|
||||
}
|
||||
},
|
||||
getFileUrl(files) {
|
||||
const file = Array.isArray(files) && files.length ? files[0] : null
|
||||
if (!file) return ""
|
||||
return file.url || file.response?.data || file.data || ""
|
||||
},
|
||||
getFileExt(files) {
|
||||
const file = Array.isArray(files) && files.length ? files[0] : null
|
||||
if (!file) return ""
|
||||
const name = file.name || file.url || file.response?.data || ""
|
||||
const index = name.lastIndexOf(".")
|
||||
return index > -1 ? name.substring(index + 1).toLowerCase() : ""
|
||||
},
|
||||
formatDurationMinutes(durationSeconds) {
|
||||
const seconds = Number(durationSeconds || 0)
|
||||
return seconds > 0 ? Math.ceil(seconds / 60) : 0
|
||||
},
|
||||
formatDurationSeconds(durationSeconds) {
|
||||
const seconds = Math.ceil(Number(durationSeconds || 0))
|
||||
return seconds > 0 ? seconds + "秒" : "0秒"
|
||||
},
|
||||
autoFillMediaDuration() {
|
||||
if (!["video", "audio"].includes(this.resourceForm.resourceType)) return
|
||||
const url = this.getFileUrl(this.resourceForm.fileData)
|
||||
if (!url) return
|
||||
const media = document.createElement(this.resourceForm.resourceType === "video" ? "video" : "audio")
|
||||
media.preload = "metadata"
|
||||
media.onloadedmetadata = () => {
|
||||
window.URL.revokeObjectURL(media.src)
|
||||
if (isFinite(media.duration) && media.duration > 0) {
|
||||
// 媒体时长按秒保存,避免先取整到分钟再换算导致实际时长被放大。
|
||||
this.$set(this.resourceForm, "durationSeconds", Math.ceil(media.duration))
|
||||
}
|
||||
}
|
||||
media.onerror = () => {
|
||||
window.URL.revokeObjectURL(media.src)
|
||||
}
|
||||
media.src = url
|
||||
},
|
||||
validResourceExt(resourceType, fileExt) {
|
||||
const item = this.resourceTypeOptions.find(v => v.code === resourceType)
|
||||
if (!item || !item.accept) return true
|
||||
return item.accept.split(",").map(v => v.replace(".", "").toLowerCase()).includes(fileExt)
|
||||
},
|
||||
getResourceTypeName(code) {
|
||||
const item = this.resourceTypeOptions.find(v => v.code === code)
|
||||
return item ? item.name : code
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
"resourceForm.fileData": {
|
||||
handler() {
|
||||
this.autoFillMediaDuration()
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
document.addEventListener("click", () => this.contextVisible = false)
|
||||
},
|
||||
async created() {
|
||||
await this.loadCourseTypes()
|
||||
const courseId = this.getQueryParam("courseId")
|
||||
if (courseId) {
|
||||
this.currentCourseId = courseId
|
||||
this.courseQuery.courseId = courseId
|
||||
await this.loadCourses(true)
|
||||
this.$delete(this.courseQuery, "courseId")
|
||||
} else {
|
||||
await this.loadCourses()
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,12 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<el-card shadow="never">
|
||||
<div slot="header">课程介绍</div>
|
||||
<el-empty description="课程介绍页面后续设计"></el-empty>
|
||||
</el-card>
|
||||
</div>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,350 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
.course-display {
|
||||
padding: 8px 10px 22px;
|
||||
background: #f4f5f7;
|
||||
min-height: calc(100vh - 88px);
|
||||
}
|
||||
.course-filter {
|
||||
background: #fff;
|
||||
border: 1px solid #e5e8ef;
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.filter-row {
|
||||
display: flex;
|
||||
min-height: 70px;
|
||||
border-bottom: 1px solid #e8ebf0;
|
||||
}
|
||||
.filter-row:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
.filter-label {
|
||||
width: 132px;
|
||||
padding: 20px 16px;
|
||||
background: #eef5ff;
|
||||
color: #1f2d3d;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
.filter-options {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 18px;
|
||||
padding: 16px 18px;
|
||||
}
|
||||
.filter-chip {
|
||||
min-width: 76px;
|
||||
height: 32px;
|
||||
padding: 0 18px;
|
||||
border: 0;
|
||||
border-radius: 9px;
|
||||
background: transparent;
|
||||
color: #526070;
|
||||
cursor: pointer;
|
||||
line-height: 32px;
|
||||
text-align: center;
|
||||
}
|
||||
.filter-chip.active {
|
||||
background: #1e63e9;
|
||||
color: #fff;
|
||||
font-weight: 600;
|
||||
}
|
||||
.course-result-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.course-count {
|
||||
color: #1f2d3d;
|
||||
}
|
||||
.course-count span {
|
||||
color: #2468f2;
|
||||
padding: 0 4px;
|
||||
}
|
||||
.course-search {
|
||||
width: 310px;
|
||||
}
|
||||
.course-list {
|
||||
background: #fff;
|
||||
border-top: 1px solid #dfe4ec;
|
||||
}
|
||||
.course-item {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 18px 0;
|
||||
border-bottom: 1px solid #dfe4ec;
|
||||
}
|
||||
.course-cover {
|
||||
position: relative;
|
||||
width: 270px;
|
||||
height: 150px;
|
||||
flex: 0 0 270px;
|
||||
background: #e9eef5;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.course-cover img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.course-cover-empty {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #8a97a8;
|
||||
background: linear-gradient(135deg, #eaf0f8, #d8e3f2);
|
||||
font-weight: 600;
|
||||
}
|
||||
.course-cover-tag {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 12px;
|
||||
min-width: 70px;
|
||||
height: 24px;
|
||||
padding: 0 10px;
|
||||
background: #f5a400;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
border-radius: 12px 0 0 12px;
|
||||
}
|
||||
.course-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding-right: 12px;
|
||||
}
|
||||
.course-title {
|
||||
display: inline-block;
|
||||
margin: 4px 0 8px;
|
||||
color: #111827;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
}
|
||||
.course-title:hover {
|
||||
color: #006fc9;
|
||||
}
|
||||
.course-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 18px;
|
||||
color: #8b96a6;
|
||||
font-size: 13px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.course-intro {
|
||||
color: #4f5f73;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 12px;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
.course-time {
|
||||
color: #8b96a6;
|
||||
font-size: 13px;
|
||||
}
|
||||
.recommend-tags {
|
||||
display: inline-flex;
|
||||
gap: 6px;
|
||||
margin-left: 8px;
|
||||
vertical-align: 2px;
|
||||
}
|
||||
.course-empty {
|
||||
background: #fff;
|
||||
padding: 80px 0;
|
||||
text-align: center;
|
||||
color: #8b96a6;
|
||||
border-top: 1px solid #dfe4ec;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="course-display" v-cloak>
|
||||
<div class="course-filter">
|
||||
<div class="filter-row">
|
||||
<div class="filter-label">推荐标识</div>
|
||||
<div class="filter-options">
|
||||
<button class="filter-chip" :class="{active: !query.recommendFlag}" @click="selectRecommend('')">全部</button>
|
||||
<button
|
||||
v-for="item in recommendOptions"
|
||||
:key="item.code"
|
||||
class="filter-chip"
|
||||
:class="{active: query.recommendFlag === item.code}"
|
||||
@click="selectRecommend(item.code)">
|
||||
{{item.name}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="filter-row">
|
||||
<div class="filter-label">课程类型</div>
|
||||
<div class="filter-options">
|
||||
<button class="filter-chip" :class="{active: !query.courseTypeId}" @click="selectCourseType('')">全部</button>
|
||||
<button
|
||||
v-for="item in courseTypeOptions"
|
||||
:key="item.id"
|
||||
class="filter-chip"
|
||||
:class="{active: query.courseTypeId === item.id}"
|
||||
@click="selectCourseType(item.id)">
|
||||
{{item.typeName}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="course-result-bar">
|
||||
<div class="course-count">为您找到相关课程<span>{{pageForm.totalCount || 0}}</span>门</div>
|
||||
<el-input
|
||||
class="course-search"
|
||||
v-model="query.keyword"
|
||||
clearable
|
||||
placeholder="搜索关键字"
|
||||
prefix-icon="el-icon-search"
|
||||
@keyup.enter.native="doSearch"
|
||||
@clear="doSearch">
|
||||
<el-button slot="append" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
</el-input>
|
||||
</div>
|
||||
|
||||
<div v-if="pageForm.list && pageForm.list.length" class="course-list">
|
||||
<div v-for="course in pageForm.list" :key="course.id" class="course-item">
|
||||
<div class="course-cover">
|
||||
<img v-if="getCoverUrl(course.cover)" :src="getCoverUrl(course.cover)" :alt="course.courseName">
|
||||
<div v-else class="course-cover-empty">{{course.courseTypeName || '课程'}}</div>
|
||||
<div v-if="firstRecommendName(course.recommendFlags)" class="course-cover-tag">{{firstRecommendName(course.recommendFlags)}}</div>
|
||||
</div>
|
||||
<div class="course-info">
|
||||
<div>
|
||||
<span class="course-title" @click="openDetail(course)">{{course.courseName}}</span>
|
||||
<span class="recommend-tags">
|
||||
<el-tag v-for="code in splitFlags(course.recommendFlags)" :key="code" size="mini" type="warning">{{getRecommendName(code)}}</el-tag>
|
||||
</span>
|
||||
</div>
|
||||
<div class="course-meta">
|
||||
<span><i class="el-icon-office-building"></i> {{course.courseTypeName || '未设置课程类型'}}</span>
|
||||
<span><i class="el-icon-user"></i> {{course.lecturerName || '未设置讲师'}}</span>
|
||||
</div>
|
||||
<div class="course-intro">{{course.courseIntro || '暂无课程介绍'}}</div>
|
||||
<div class="course-time">
|
||||
<i class="el-icon-time"></i>
|
||||
<span v-if="course.openType === 'long_term'">长期开放</span>
|
||||
<span v-else>{{course.startTimeText || '-'}} 至 {{course.endTimeText || '-'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="course-empty">暂无相关课程</div>
|
||||
|
||||
<el-pagination
|
||||
class="mt20"
|
||||
background
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:current-page.sync="pageForm.pageNumber"
|
||||
:page-size.sync="pageForm.pageSize"
|
||||
:total="pageForm.totalCount"
|
||||
@size-change="pageData"
|
||||
@current-change="pageData">
|
||||
</el-pagination>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
query: {
|
||||
keyword: "",
|
||||
courseTypeId: "",
|
||||
recommendFlag: ""
|
||||
},
|
||||
courseTypeOptions: [],
|
||||
recommendOptions: [],
|
||||
pageForm: {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
list: []
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async pageData() {
|
||||
const resp = await this.$axios.post(loc() + "/pageData", Object.assign({}, this.query, {
|
||||
pageNumber: this.pageForm.pageNumber,
|
||||
pageSize: this.pageForm.pageSize
|
||||
}))
|
||||
if (resp.code === 0) {
|
||||
this.pageForm = resp.data
|
||||
}
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
selectRecommend(code) {
|
||||
this.query.recommendFlag = code
|
||||
this.doSearch()
|
||||
},
|
||||
selectCourseType(id) {
|
||||
this.query.courseTypeId = id
|
||||
this.doSearch()
|
||||
},
|
||||
async loadOptions() {
|
||||
const typeResp = await this.$axios.post(loc() + "/courseTypes")
|
||||
if (typeResp.code === 0) {
|
||||
this.courseTypeOptions = typeResp.data
|
||||
}
|
||||
const recommendResp = await this.$axios.post(loc() + "/recommendOptions")
|
||||
if (recommendResp.code === 0) {
|
||||
this.recommendOptions = recommendResp.data
|
||||
}
|
||||
},
|
||||
getCoverUrl(cover) {
|
||||
if (!cover) return ""
|
||||
if (Array.isArray(cover)) {
|
||||
return cover.length ? (cover[0].url || cover[0].response?.data || cover[0].data || "") : ""
|
||||
}
|
||||
if (typeof cover === "string" && cover.trim().startsWith("[")) {
|
||||
try {
|
||||
const files = JSON.parse(cover)
|
||||
return files.length ? (files[0].url || files[0].response?.data || files[0].data || "") : ""
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return cover
|
||||
},
|
||||
splitFlags(flags) {
|
||||
return flags ? flags.split(",").filter(Boolean) : []
|
||||
},
|
||||
getRecommendName(code) {
|
||||
const item = this.recommendOptions.find(v => v.code === code)
|
||||
return item ? item.name : code
|
||||
},
|
||||
firstRecommendName(flags) {
|
||||
const codes = this.splitFlags(flags)
|
||||
return codes.length ? this.getRecommendName(codes[0]) : ""
|
||||
},
|
||||
openDetail(course) {
|
||||
window.location.href = loc() + "/study?id=" + course.id
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.loadOptions()
|
||||
await this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,213 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
[v-cloak] { display: none; }
|
||||
#sub-app-container-main-content { padding: 0 !important; overflow: hidden; background: #f5f8fc; }
|
||||
#sub-app-container-main-content-body { height: 100%; overflow: hidden; }
|
||||
.learning-list-page { height: 100%; min-height: 0; overflow: hidden; color: #17233d; background: #f5f8fc; }
|
||||
.learning-body { width: min(1180px, calc(100% - 24px)); height: 100%; min-height: 0; margin: 0 auto; padding: 12px 0 22px; display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: 30px; box-sizing: border-box; }
|
||||
.filter-panel { min-height: 0; overflow: hidden; display: flex; flex-direction: column; gap: 16px; }
|
||||
.filter-group { padding: 16px; flex: none; background: #fff; border-radius: 9px; box-shadow: 0 6px 22px rgba(31, 70, 121, .08); }
|
||||
.filter-group + .filter-group { margin-top: 0; }
|
||||
.filter-title { margin: 2px 0 10px; padding-left: 9px; position: relative; color: #24344e; font-size: 14px; font-weight: 700; line-height: 20px; }
|
||||
.filter-title::before { content: ""; width: 3px; height: 14px; position: absolute; left: 0; top: 3px; border-radius: 2px; background: #1677ff; }
|
||||
.filter-option { height: 34px; padding: 0 10px; display: flex; align-items: center; color: #45536a; border-radius: 6px; cursor: pointer; font-size: 14px; transition: .2s; }
|
||||
.filter-option-icon { width: 18px; margin-right: 10px; color: #6e7d94; font-size: 16px; text-align: center; }
|
||||
.filter-option:hover { color: #1677ff; background: #edf5ff; }
|
||||
.filter-option.active { color: #1677ff; background: #eaf3ff; font-weight: 600; }
|
||||
.filter-option.active .filter-option-icon { color: #1677ff; }
|
||||
.course-content { min-width: 0; min-height: 0; overflow-y: auto; padding: 0 10px 6px 0; scrollbar-gutter: stable; }
|
||||
.course-content::-webkit-scrollbar { width: 7px; }
|
||||
.course-content::-webkit-scrollbar-thumb { border-radius: 5px; background: #d6e1ef; }
|
||||
.course-toolbar { height: 38px; margin-bottom: 10px; position: sticky; top: 0; z-index: 2; display: flex; align-items: flex-start; justify-content: space-between; color: #4e5d74; font-size: 14px; background: #f5f8fc; }
|
||||
.course-toolbar > span { padding-top: 8px; font-size: 15px; font-weight: 600; }
|
||||
.course-toolbar .el-select { width: 122px; }
|
||||
.course-toolbar .el-input__inner { border-color: #e2e9f2; border-radius: 6px; background: #fff; color: #53627a; }
|
||||
.course-list { display: flex; flex-direction: column; gap: 16px; }
|
||||
.course-card { min-height: 160px; display: grid; grid-template-columns: 236px minmax(0, 1fr) 112px; overflow: hidden; background: #fff; border: 0; border-radius: 10px; box-shadow: 0 5px 18px rgba(30, 66, 115, .08); transition: box-shadow .2s, transform .2s; }
|
||||
.course-card:hover { transform: translateY(-1px); box-shadow: 0 9px 24px rgba(30, 67, 119, .12); }
|
||||
.course-cover { position: relative; min-height: 160px; overflow: hidden; background: #eaf5ee; }
|
||||
.course-cover img { width: 100%; height: 100%; display: block; object-fit: cover; }
|
||||
.course-cover-empty { width: 100%; height: 100%; min-height: 160px; display: flex; align-items: center; justify-content: center; color: #77a28a; font-size: 18px; background: linear-gradient(135deg, #e5f5ea, #f3faf6); }
|
||||
.course-cover-empty i { margin-right: 8px; font-size: 32px; }
|
||||
.course-card:nth-child(3n+2) .course-cover-empty { color: #7188ae; background: linear-gradient(135deg, #e7effc, #f3f7fe); }
|
||||
.course-card:nth-child(3n) .course-cover-empty { color: #bc8768; background: linear-gradient(135deg, #fff0e7, #fff8f3); }
|
||||
.course-info { min-width: 0; padding: 18px 18px 14px 22px; box-sizing: border-box; }
|
||||
.course-heading { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||
.course-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #17243b; font-size: 19px; font-weight: 700; cursor: pointer; }
|
||||
.course-type { flex: none; color: #98a2b3; font-size: 13px; }
|
||||
.recommend-tag { flex: none; padding: 2px 5px; color: #ff4d4f; background: #fff1f0; border-radius: 3px; font-size: 12px; }
|
||||
.course-intro { height: 43px; margin-top: 7px; overflow: hidden; color: #59677c; font-size: 14px; line-height: 22px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; }
|
||||
.course-meta { margin-top: 11px; display: flex; align-items: center; flex-wrap: wrap; gap: 18px; color: #91a0b7; font-size: 13px; }
|
||||
.course-meta span { display: inline-flex; align-items: center; gap: 4px; }
|
||||
.course-meta i { font-size: 14px; }
|
||||
.course-action { display: flex; align-items: center; justify-content: center; padding-right: 18px; }
|
||||
.course-action .el-button { width: 84px; border-radius: 18px; color: #1677ff; border-color: #a9d0ff; background: #f5faff; font-weight: 600; }
|
||||
.course-empty { height: 280px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #9aa7ba; background: #fff; border: 1px solid #e7e9ed; border-radius: 12px; }
|
||||
.course-empty i { margin-bottom: 12px; font-size: 45px; }
|
||||
.course-pagination { margin: 24px 0 4px; text-align: right; }
|
||||
@media (max-width: 980px) { .learning-body { grid-template-columns: 180px minmax(0, 1fr); gap: 18px; } .course-card { grid-template-columns: 190px minmax(0, 1fr) 96px; } }
|
||||
@media (max-width: 820px) { #sub-app-container-main-content, #sub-app-container-main-content-body { overflow-y: auto; } .learning-list-page { height: auto; min-height: 100%; overflow: visible; } .learning-body { height: auto; min-height: auto; grid-template-columns: 1fr; padding-bottom: 24px; overflow: visible; } .filter-panel { display: flex; flex-direction: row; gap: 12px; overflow-x: auto; } .filter-group { min-width: 180px; } .course-content { overflow: visible; padding-right: 0; } .course-toolbar { position: static; } .course-card { grid-template-columns: 170px minmax(0, 1fr); } .course-action { grid-column: 2; justify-content: flex-end; padding: 0 16px 14px; } }
|
||||
</style>
|
||||
|
||||
<div id="app" class="learning-list-page" v-cloak>
|
||||
<div class="learning-body">
|
||||
<aside class="filter-panel">
|
||||
<div class="filter-group">
|
||||
<div class="filter-title">课程分类</div>
|
||||
<div class="filter-option" :class="{active: !query.courseTypeId}" @click="selectCourseType('')"><i class="filter-option-icon el-icon-menu"></i>全部</div>
|
||||
<div v-for="item in courseTypeOptions" :key="item.id" class="filter-option" :class="{active: query.courseTypeId === item.id}" @click="selectCourseType(item.id)"><i class="filter-option-icon el-icon-collection-tag"></i>{{item.typeName}}</div>
|
||||
</div>
|
||||
<div v-if="recommendOptions.length" class="filter-group">
|
||||
<div class="filter-title">推荐类型</div>
|
||||
<div class="filter-option" :class="{active: !query.recommendFlag}" @click="selectRecommend('')"><i class="filter-option-icon el-icon-star-on"></i>全部</div>
|
||||
<div v-for="item in recommendOptions" :key="item.code" class="filter-option" :class="{active: query.recommendFlag === item.code}" @click="selectRecommend(item.code)"><i class="filter-option-icon el-icon-star-off"></i>{{item.name}}</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="course-content" v-loading="loading">
|
||||
<div class="course-toolbar">
|
||||
<span>共 {{pageForm.totalCount || 0}} 门课程</span>
|
||||
<div>
|
||||
<span style="margin-right: 8px">排序:</span>
|
||||
<el-select v-model="query.sortType" size="small" @change="doSearch">
|
||||
<el-option label="综合排序" value="default"></el-option>
|
||||
<el-option label="观看最多" value="view_count"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="pageForm.list && pageForm.list.length" class="course-list">
|
||||
<article v-for="course in pageForm.list" :key="course.id" class="course-card">
|
||||
<div class="course-cover">
|
||||
<img v-if="getCoverUrl(course.cover)" :src="getCoverUrl(course.cover)" :alt="course.courseName">
|
||||
<div v-else class="course-cover-empty"><i class="el-icon-reading"></i>{{course.courseTypeName || '在线课程'}}</div>
|
||||
</div>
|
||||
<div class="course-info">
|
||||
<div class="course-heading">
|
||||
<span class="course-name" @click="startStudy(course)">{{course.courseName}}</span>
|
||||
<span class="course-type">{{course.courseTypeName || '未分类'}}</span>
|
||||
<span v-if="firstRecommendName(course.recommendFlags)" class="recommend-tag">{{firstRecommendName(course.recommendFlags)}}</span>
|
||||
</div>
|
||||
<div class="course-intro">{{course.courseIntro || '暂无课程介绍'}}</div>
|
||||
<div class="course-meta">
|
||||
<span><i class="el-icon-collection"></i>{{course.resourceCount || 0}} 课节</span>
|
||||
<span><i class="el-icon-time"></i>{{formatDuration(course.totalDurationSeconds)}}</span>
|
||||
<span><i class="el-icon-user"></i>{{course.lecturerName || '未设置讲师'}}</span>
|
||||
<span><i class="el-icon-view"></i>{{course.viewCount || 0}}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-action">
|
||||
<el-button size="small" type="primary" plain @click="startStudy(course)">开始学习</el-button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else-if="!loading" class="course-empty"><i class="el-icon-reading"></i><span>暂无相关课程</span></div>
|
||||
|
||||
<el-pagination
|
||||
v-if="pageForm.totalCount > pageForm.pageSize"
|
||||
class="course-pagination"
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
:current-page.sync="pageForm.pageNumber"
|
||||
:page-size="pageForm.pageSize"
|
||||
:total="pageForm.totalCount"
|
||||
@current-change="pageData">
|
||||
</el-pagination>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
query: { keyword: "", courseTypeId: "", recommendFlag: "", sortType: "default" },
|
||||
courseTypeOptions: [],
|
||||
recommendOptions: [],
|
||||
pageForm: { pageNumber: 1, pageSize: 10, totalCount: 0, list: [] }
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
this.loading = true
|
||||
this.$axios.post(loc() + "/pageData", Object.assign({}, this.query, {
|
||||
pageNumber: this.pageForm.pageNumber,
|
||||
pageSize: this.pageForm.pageSize
|
||||
})).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.pageForm = resp.data
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
loadOptions() {
|
||||
return Promise.all([
|
||||
this.$axios.post(loc() + "/courseTypes"),
|
||||
this.$axios.post(loc() + "/recommendOptions")
|
||||
]).then((responses) => {
|
||||
this.courseTypeOptions = responses[0].code === 0 ? responses[0].data : []
|
||||
this.recommendOptions = responses[1].code === 0 ? responses[1].data : []
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
selectCourseType(id) {
|
||||
this.query.courseTypeId = id
|
||||
this.doSearch()
|
||||
},
|
||||
selectRecommend(code) {
|
||||
this.query.recommendFlag = code
|
||||
this.doSearch()
|
||||
},
|
||||
getCoverUrl(cover) {
|
||||
if (!cover) return ""
|
||||
if (Array.isArray(cover)) return cover.length ? (cover[0].url || cover[0].data || "") : ""
|
||||
if (typeof cover === "string" && cover.trim().startsWith("[")) {
|
||||
try {
|
||||
const files = JSON.parse(cover)
|
||||
return files.length ? (files[0].url || files[0].data || "") : ""
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return cover
|
||||
},
|
||||
splitFlags(flags) {
|
||||
return flags ? flags.split(",").filter(Boolean) : []
|
||||
},
|
||||
firstRecommendName(flags) {
|
||||
const codes = this.splitFlags(flags)
|
||||
if (!codes.length) return ""
|
||||
const item = this.recommendOptions.find((option) => option.code === codes[0])
|
||||
return item ? item.name : codes[0]
|
||||
},
|
||||
formatDuration(seconds) {
|
||||
const total = Number(seconds) || 0
|
||||
if (total < 3600) return Math.max(1, Math.ceil(total / 60)) + " 分钟"
|
||||
const hours = Math.floor(total / 3600)
|
||||
const minutes = Math.ceil((total % 3600) / 60)
|
||||
return minutes ? hours + "小时" + minutes + "分钟" : hours + "小时"
|
||||
},
|
||||
startStudy(course) {
|
||||
window.location.href = loc() + "/study?id=" + encodeURIComponent(course.id || "")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadOptions().then(() => {
|
||||
this.pageData()
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,755 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
#sidebar-menu,
|
||||
#menu-toggle-btn,
|
||||
#menu-overlay {
|
||||
display: none !important;
|
||||
}
|
||||
#sub-app-container-main-content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
#sub-app-container-main-content-body {
|
||||
height: 100%;
|
||||
}
|
||||
.study-page {
|
||||
min-height: calc(100vh - 50px);
|
||||
background: #eef3f8;
|
||||
}
|
||||
.study-header {
|
||||
height: 150px;
|
||||
margin: 12px 14px 0;
|
||||
padding: 20px 24px;
|
||||
color: #1f2937;
|
||||
background: linear-gradient(120deg, #ffffff 0%, #f4f8ff 58%, #eaf2ff 100%);
|
||||
border: 1px solid #dbe7f5;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 6px 18px rgba(31, 64, 116, .08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 22px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.study-back {
|
||||
align-self: flex-start;
|
||||
margin-left: auto;
|
||||
order: 5;
|
||||
color: #1e63c8;
|
||||
border-color: #b8d0f2;
|
||||
background: #fff;
|
||||
}
|
||||
.study-cover {
|
||||
width: 190px;
|
||||
height: 102px;
|
||||
object-fit: cover;
|
||||
background: #eef4fb;
|
||||
border: 1px solid #d7e3f1;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(31, 64, 116, .12);
|
||||
}
|
||||
.study-cover-empty {
|
||||
width: 190px;
|
||||
height: 102px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #eef4fb;
|
||||
color: #6b7d90;
|
||||
border: 1px solid #d7e3f1;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 12px rgba(31, 64, 116, .12);
|
||||
font-weight: 600;
|
||||
}
|
||||
.study-course-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.study-title {
|
||||
font-size: 28px;
|
||||
line-height: 1.2;
|
||||
font-weight: 700;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.study-title .el-tag {
|
||||
margin-left: 8px;
|
||||
vertical-align: 5px;
|
||||
}
|
||||
.study-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px 26px;
|
||||
color: #526376;
|
||||
font-size: 14px;
|
||||
}
|
||||
.study-body {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
height: calc(100vh - 212px);
|
||||
padding: 14px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.study-tree-panel {
|
||||
width: 340px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.study-tree-title {
|
||||
height: 54px;
|
||||
padding: 0 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
color: #1768e5;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
border-bottom: 1px solid #edf1f7;
|
||||
}
|
||||
.study-tree {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
}
|
||||
.study-node {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
max-width: 100%;
|
||||
gap: 6px;
|
||||
}
|
||||
.study-node-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.study-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.resource-head {
|
||||
min-height: 54px;
|
||||
padding: 0 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid #edf1f7;
|
||||
}
|
||||
.resource-title {
|
||||
color: #1f2937;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.resource-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.study-progress {
|
||||
width: 180px;
|
||||
}
|
||||
.resource-view {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
background: #f8fafc;
|
||||
}
|
||||
.media-player {
|
||||
width: 100%;
|
||||
max-height: calc(100vh - 300px);
|
||||
background: #000;
|
||||
}
|
||||
.audio-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 320px;
|
||||
background: linear-gradient(135deg, #132544, #0a1224);
|
||||
border-radius: 6px;
|
||||
}
|
||||
.audio-wrap audio {
|
||||
width: 80%;
|
||||
}
|
||||
.image-preview {
|
||||
max-width: 100%;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
}
|
||||
.doc-frame {
|
||||
width: 100%;
|
||||
height: calc(100vh - 275px);
|
||||
border: 0;
|
||||
background: #fff;
|
||||
}
|
||||
.file-fallback {
|
||||
padding: 60px 20px;
|
||||
text-align: center;
|
||||
color: #667085;
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="app" class="study-page" v-cloak>
|
||||
<div class="study-header">
|
||||
<el-button class="study-back" icon="el-icon-back" size="small" @click="goBack">返回</el-button>
|
||||
<img v-if="coverUrl" class="study-cover" :src="coverUrl" :alt="course.courseName">
|
||||
<div v-else class="study-cover-empty">课程图片</div>
|
||||
<div class="study-course-main">
|
||||
<div class="study-title">
|
||||
{{course.courseName || '课程学习'}}
|
||||
<el-tag v-for="code in splitFlags(course.recommendFlags)" :key="code" size="small" type="warning">{{getRecommendName(code)}}</el-tag>
|
||||
</div>
|
||||
<div class="study-meta">
|
||||
<span><i class="el-icon-user"></i> 授课讲师:{{course.lecturerName || '未设置'}}</span>
|
||||
<span><i class="el-icon-date"></i> {{course.openType === 'long_term' ? '长期开放' : ((course.startTimeText || '-') + ' 至 ' + (course.endTimeText || '-'))}}</span>
|
||||
<span><i class="el-icon-collection-tag"></i> 课程类型:{{course.courseTypeName || '未设置'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="study-body">
|
||||
<div class="study-tree-panel">
|
||||
<div class="study-tree-title">
|
||||
<span>课程安排</span>
|
||||
<el-button type="text" size="mini" @click="collapseAll">收起</el-button>
|
||||
</div>
|
||||
<div class="study-tree">
|
||||
<el-tree
|
||||
ref="studyTree"
|
||||
:data="treeData"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
:expand-on-click-node="false"
|
||||
:props="{children:'children', label:'title'}"
|
||||
@node-click="nodeClick">
|
||||
<span slot-scope="{ data }" class="study-node">
|
||||
<i :class="getNodeIcon(data)"></i>
|
||||
<span class="study-node-title">{{data.title}}</span>
|
||||
<el-tag v-if="data.required" size="mini" type="warning">必学</el-tag>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="study-content">
|
||||
<div class="resource-head">
|
||||
<div class="resource-title">{{selectedResource.title || '请选择课程资料'}}</div>
|
||||
<div class="resource-actions">
|
||||
<el-progress
|
||||
v-if="selectedResource.id"
|
||||
class="study-progress"
|
||||
:percentage="recordProgress"
|
||||
:stroke-width="8">
|
||||
</el-progress>
|
||||
<el-tag v-if="studying" size="mini" type="success">学习中 {{recordStudyTime}}</el-tag>
|
||||
<el-button v-if="selectedResource.id && !studying" size="mini" type="primary" icon="el-icon-video-play" @click="startStudy">开始学习</el-button>
|
||||
<el-button v-if="studying" size="mini" type="danger" icon="el-icon-video-pause" @click="finishStudy(false)">结束学习</el-button>
|
||||
<el-button v-if="fileUrl" size="mini" type="text" icon="el-icon-download" @click="openFile">打开原文件</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="resource-view">
|
||||
<template v-if="selectedResource.id">
|
||||
<video
|
||||
v-if="selectedResource.resourceType === 'video'"
|
||||
ref="mediaPlayer"
|
||||
class="media-player"
|
||||
:src="fileUrl"
|
||||
controls
|
||||
controlslist="nodownload"
|
||||
@play="mediaPlay"
|
||||
@ended="mediaEnded"
|
||||
@timeupdate="saveProgress"
|
||||
@loadedmetadata="mediaReady">
|
||||
</video>
|
||||
<div v-else-if="selectedResource.resourceType === 'audio'" class="audio-wrap">
|
||||
<audio
|
||||
ref="mediaPlayer"
|
||||
:src="fileUrl"
|
||||
controls
|
||||
controlslist="nodownload"
|
||||
@play="mediaPlay"
|
||||
@ended="mediaEnded"
|
||||
@timeupdate="saveProgress"
|
||||
@loadedmetadata="mediaReady">
|
||||
</audio>
|
||||
</div>
|
||||
<img v-else-if="selectedResource.resourceType === 'image'" class="image-preview" :src="fileUrl" :alt="selectedResource.title">
|
||||
<iframe v-else-if="canInlinePreview(selectedResource)" class="doc-frame" :src="inlinePreviewUrl"></iframe>
|
||||
<div v-else class="file-fallback">
|
||||
<file-preview :files="selectedResource.fileData" complete_result></file-preview>
|
||||
<el-button class="mt20" type="primary" icon="el-icon-view" @click="openFile">打开资料</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-empty v-else description="请选择左侧课程资料"></el-empty>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
data() {
|
||||
return {
|
||||
courseId: "",
|
||||
apiBase: "/platform/learning/course/display",
|
||||
recordApi: "/platform/learning/study/record",
|
||||
course: {},
|
||||
treeData: [],
|
||||
selectedResource: {},
|
||||
recommendOptions: [],
|
||||
studying: false,
|
||||
currentSegmentId: "",
|
||||
recordProgress: 0,
|
||||
recordStudyTime: "0秒",
|
||||
pendingSeconds: 0,
|
||||
heartbeatTimer: null,
|
||||
startingStudy: false,
|
||||
autoResumeKey: "",
|
||||
lastPositionSyncAt: 0,
|
||||
lastActiveAt: Date.now(),
|
||||
inactiveLimit: 60000
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
coverUrl() {
|
||||
return this.getFileUrl(this.course.cover)
|
||||
},
|
||||
fileUrl() {
|
||||
return this.getFileUrl(this.selectedResource.fileData)
|
||||
},
|
||||
inlinePreviewUrl() {
|
||||
const id = this.getFileId(this.selectedResource.fileData)
|
||||
return id ? this.apiBase + "/pdfPreview?id=" + encodeURIComponent(id) : this.fileUrl
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getQuery(name) {
|
||||
return new URLSearchParams(window.location.search).get(name) || ""
|
||||
},
|
||||
async loadCourse() {
|
||||
const resp = await this.$axios.post(this.apiBase + "/courseInfo", { id: this.courseId })
|
||||
if (resp.code === 0) {
|
||||
this.course = resp.data
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
async loadTree() {
|
||||
const resp = await this.$axios.post(this.apiBase + "/studyTree", { courseId: this.courseId })
|
||||
if (resp.code === 0) {
|
||||
this.treeData = resp.data || []
|
||||
const first = this.findFirstResource(this.treeData)
|
||||
if (first) {
|
||||
this.nodeClick(first)
|
||||
this.$nextTick(() => this.$refs.studyTree && this.$refs.studyTree.setCurrentKey(first.id))
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadRecommendOptions() {
|
||||
const resp = await this.$axios.post(this.apiBase + "/recommendOptions")
|
||||
if (resp.code === 0) {
|
||||
this.recommendOptions = resp.data
|
||||
}
|
||||
},
|
||||
findFirstResource(nodes) {
|
||||
for (const node of nodes || []) {
|
||||
if (node.type === "resource") return node
|
||||
const child = this.findFirstResource(node.children || [])
|
||||
if (child) return child
|
||||
}
|
||||
return null
|
||||
},
|
||||
nodeClick(data) {
|
||||
if (data.type !== "resource") return
|
||||
if (this.studying && this.selectedResource.id !== data.id) {
|
||||
this.$message.warning("请先结束当前章节资料的学习")
|
||||
return
|
||||
}
|
||||
this.selectedResource = data
|
||||
this.recordProgress = data.progressPercent || 0
|
||||
this.recordStudyTime = this.formatSeconds(Number(data.studySeconds || 0))
|
||||
},
|
||||
getNodeIcon(data) {
|
||||
if (data.type === "resource") {
|
||||
const map = {
|
||||
video: "el-icon-video-camera",
|
||||
audio: "el-icon-headset",
|
||||
image: "el-icon-picture-outline",
|
||||
pdf: "el-icon-document",
|
||||
word: "el-icon-document",
|
||||
ppt: "el-icon-document"
|
||||
}
|
||||
return map[data.resourceType] || "el-icon-paperclip"
|
||||
}
|
||||
return data.nodeType === "chapter" ? "el-icon-folder" : "el-icon-notebook-2"
|
||||
},
|
||||
getFileUrl(value) {
|
||||
if (!value) return ""
|
||||
if (Array.isArray(value)) {
|
||||
return value.length ? (value[0].url || value[0].response?.data || value[0].data || "") : ""
|
||||
}
|
||||
if (typeof value === "string" && value.trim().startsWith("[")) {
|
||||
try {
|
||||
const files = JSON.parse(value)
|
||||
return files.length ? (files[0].url || files[0].response?.data || files[0].data || "") : ""
|
||||
} catch (e) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return value
|
||||
},
|
||||
getFileId(value) {
|
||||
const url = this.getFileUrl(value)
|
||||
if (!url) return ""
|
||||
const matched = url.match(/[?&]id=([^&]+)/)
|
||||
if (matched) return decodeURIComponent(matched[1])
|
||||
if (!url.includes("/") && !url.includes(".")) return url
|
||||
return ""
|
||||
},
|
||||
canInlinePreview(resource) {
|
||||
const type = resource.resourceType
|
||||
const ext = (resource.fileExt || "").toLowerCase()
|
||||
return ["pdf", "ppt", "word"].includes(type) || ["pdf", "ppt", "pptx", "doc", "docx"].includes(ext)
|
||||
},
|
||||
progressKey() {
|
||||
return "learning-progress-" + this.courseId + "-" + this.selectedResource.id
|
||||
},
|
||||
mediaReady() {
|
||||
this.askResume()
|
||||
},
|
||||
askResume() {
|
||||
if (!["video", "audio"].includes(this.selectedResource.resourceType)) return
|
||||
if (this.studying || this.startingStudy) return
|
||||
const currentKey = this.progressKey()
|
||||
if (this.autoResumeKey === currentKey) return
|
||||
this.autoResumeKey = currentKey
|
||||
const player = this.$refs.mediaPlayer
|
||||
const localSaved = Number(localStorage.getItem(this.progressKey()) || 0)
|
||||
const serverSaved = Number(this.selectedResource.lastPositionSeconds || 0)
|
||||
const saved = Math.max(localSaved, serverSaved)
|
||||
if (!player || !saved || saved < 5 || saved >= player.duration - 5) return
|
||||
this.$confirm("检测到上次学习到 " + this.formatSeconds(saved) + ",请选择继续学习或从头开始。", "继续学习", {
|
||||
confirmButtonText: "继续学习",
|
||||
cancelButtonText: "从头开始",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
this.startStudy({ autoPlay: true, seekTo: saved })
|
||||
}).catch(() => {
|
||||
localStorage.removeItem(this.progressKey())
|
||||
this.saveServerPosition(0)
|
||||
this.startStudy({ autoPlay: true, seekTo: 0 })
|
||||
})
|
||||
},
|
||||
async mediaPlay() {
|
||||
if (this.studying || this.startingStudy) return
|
||||
await this.startStudy({ fromMedia: true })
|
||||
},
|
||||
mediaEnded() {
|
||||
this.finishStudy(false, { silent: true })
|
||||
localStorage.removeItem(this.progressKey())
|
||||
},
|
||||
async playSelectedMedia(seekTo) {
|
||||
if (!["video", "audio"].includes(this.selectedResource.resourceType)) return
|
||||
await this.$nextTick()
|
||||
const player = this.$refs.mediaPlayer
|
||||
if (!player) return
|
||||
try {
|
||||
if (typeof seekTo === "number" && !Number.isNaN(seekTo)) {
|
||||
await this.seekBeforePlay(player, seekTo)
|
||||
}
|
||||
const playPromise = player.play && player.play()
|
||||
if (playPromise && playPromise.catch) {
|
||||
await playPromise.catch(() => {
|
||||
this.$message.warning("浏览器阻止了自动播放,请点击播放器播放按钮继续")
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
this.$message.warning("播放器定位失败,请手动点击播放后继续")
|
||||
}
|
||||
},
|
||||
async seekBeforePlay(player, seekTo) {
|
||||
await this.waitForMediaReady(player)
|
||||
const target = this.normalizeSeekTarget(player, seekTo)
|
||||
if (target === null) return
|
||||
if (Math.abs((player.currentTime || 0) - target) <= 1) return
|
||||
player.pause()
|
||||
player.currentTime = target
|
||||
await this.waitForSeek(player, target, 1800)
|
||||
if (Math.abs((player.currentTime || 0) - target) > 1) {
|
||||
player.currentTime = target
|
||||
await this.waitForSeek(player, target, 1200)
|
||||
}
|
||||
},
|
||||
waitForMediaReady(player) {
|
||||
if (player.readyState >= 1 && !Number.isNaN(player.duration)) {
|
||||
return Promise.resolve()
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
const done = () => {
|
||||
player.removeEventListener("loadedmetadata", done)
|
||||
player.removeEventListener("durationchange", done)
|
||||
resolve()
|
||||
}
|
||||
player.addEventListener("loadedmetadata", done, { once: true })
|
||||
player.addEventListener("durationchange", done, { once: true })
|
||||
setTimeout(done, 2000)
|
||||
})
|
||||
},
|
||||
waitForSeek(player, target, timeout) {
|
||||
return new Promise(resolve => {
|
||||
const done = () => {
|
||||
player.removeEventListener("seeked", done)
|
||||
player.removeEventListener("timeupdate", done)
|
||||
resolve()
|
||||
}
|
||||
player.addEventListener("seeked", done, { once: true })
|
||||
player.addEventListener("timeupdate", done, { once: true })
|
||||
setTimeout(done, timeout)
|
||||
})
|
||||
},
|
||||
normalizeSeekTarget(player, seekTo) {
|
||||
const raw = Math.max(0, Number(seekTo || 0))
|
||||
if (Number.isNaN(raw)) return null
|
||||
if (!Number.isNaN(player.duration) && player.duration > 0) {
|
||||
return Math.min(raw, Math.max(0, player.duration - 1))
|
||||
}
|
||||
return raw
|
||||
},
|
||||
saveProgress() {
|
||||
if (!["video", "audio"].includes(this.selectedResource.resourceType)) return
|
||||
const player = this.$refs.mediaPlayer
|
||||
if (player && player.currentTime > 0) {
|
||||
const position = Math.floor(player.currentTime)
|
||||
localStorage.setItem(this.progressKey(), String(position))
|
||||
this.$set(this.selectedResource, "lastPositionSeconds", position)
|
||||
if (Date.now() - this.lastPositionSyncAt > 5000) {
|
||||
this.saveServerPosition(position)
|
||||
}
|
||||
}
|
||||
},
|
||||
saveServerPosition(position, useBeacon) {
|
||||
if (!this.selectedResource.id || !["video", "audio"].includes(this.selectedResource.resourceType)) return
|
||||
const seconds = Math.max(0, Math.floor(Number(position || 0)))
|
||||
this.lastPositionSyncAt = Date.now()
|
||||
if (useBeacon && navigator.sendBeacon) {
|
||||
const body = new URLSearchParams()
|
||||
body.append("courseId", this.courseId)
|
||||
body.append("resourceId", this.selectedResource.id)
|
||||
body.append("positionSeconds", String(seconds))
|
||||
const blob = new Blob([body.toString()], { type: "application/x-www-form-urlencoded;charset=UTF-8" })
|
||||
navigator.sendBeacon(this.recordApi + "/position", blob)
|
||||
return
|
||||
}
|
||||
this.$axios.post(this.recordApi + "/position", {
|
||||
courseId: this.courseId,
|
||||
resourceId: this.selectedResource.id,
|
||||
positionSeconds: seconds
|
||||
})
|
||||
},
|
||||
async startStudy(options) {
|
||||
const config = options && !options.target ? options : {}
|
||||
if (!this.selectedResource.id) {
|
||||
this.$message.warning("请选择左侧课程资料")
|
||||
return
|
||||
}
|
||||
if (this.studying) {
|
||||
if (config.autoPlay) {
|
||||
this.playSelectedMedia(config.seekTo)
|
||||
}
|
||||
return
|
||||
}
|
||||
this.startingStudy = true
|
||||
let resp
|
||||
try {
|
||||
resp = await this.$axios.post(this.recordApi + "/start", {
|
||||
courseId: this.courseId,
|
||||
resourceId: this.selectedResource.id,
|
||||
positionSeconds: typeof config.seekTo === "number" ? Math.floor(config.seekTo) : this.getMediaPosition()
|
||||
})
|
||||
} finally {
|
||||
this.startingStudy = false
|
||||
}
|
||||
if (resp.code !== 0) {
|
||||
this.$message.warning(resp.msg)
|
||||
return
|
||||
}
|
||||
this.currentSegmentId = resp.data.segmentId
|
||||
this.studying = true
|
||||
this.pendingSeconds = 0
|
||||
this.lastActiveAt = Date.now()
|
||||
this.applyRecordState(resp.data)
|
||||
this.startHeartbeatTimer()
|
||||
if (config.autoPlay !== false && !config.fromMedia) {
|
||||
this.playSelectedMedia(config.seekTo)
|
||||
}
|
||||
this.$message.success("已开始学习")
|
||||
},
|
||||
async heartbeat() {
|
||||
if (!this.studying || !this.currentSegmentId || this.pendingSeconds <= 0) return
|
||||
const seconds = this.pendingSeconds
|
||||
this.pendingSeconds = 0
|
||||
const resp = await this.$axios.post(this.recordApi + "/heartbeat", {
|
||||
segmentId: this.currentSegmentId,
|
||||
activeSeconds: seconds,
|
||||
positionSeconds: this.getMediaPosition()
|
||||
})
|
||||
if (resp.code === 0) {
|
||||
this.applyRecordState(resp.data)
|
||||
} else {
|
||||
this.stopHeartbeatTimer()
|
||||
this.studying = false
|
||||
this.currentSegmentId = ""
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
},
|
||||
async finishStudy(force, options) {
|
||||
const config = options || {}
|
||||
if (!this.currentSegmentId) return
|
||||
const segmentId = this.currentSegmentId
|
||||
const seconds = this.pendingSeconds
|
||||
this.pendingSeconds = 0
|
||||
this.stopHeartbeatTimer()
|
||||
this.studying = false
|
||||
this.currentSegmentId = ""
|
||||
if (!force) {
|
||||
this.pauseSelectedMedia()
|
||||
}
|
||||
if (force && navigator.sendBeacon) {
|
||||
this.saveServerPosition(this.getMediaPosition(), true)
|
||||
const formData = new FormData()
|
||||
formData.append("segmentId", segmentId)
|
||||
formData.append("activeSeconds", String(seconds))
|
||||
formData.append("positionSeconds", String(this.getMediaPosition()))
|
||||
navigator.sendBeacon(this.recordApi + "/finish", formData)
|
||||
return
|
||||
}
|
||||
const resp = await this.$axios.post(this.recordApi + "/finish", {
|
||||
segmentId: segmentId,
|
||||
activeSeconds: seconds,
|
||||
positionSeconds: this.getMediaPosition()
|
||||
})
|
||||
if (resp.code === 0 && resp.data) {
|
||||
this.applyRecordState(resp.data)
|
||||
if (!config.silent) {
|
||||
this.$message.success("学习已结束")
|
||||
}
|
||||
}
|
||||
},
|
||||
pauseSelectedMedia() {
|
||||
if (!["video", "audio"].includes(this.selectedResource.resourceType)) return
|
||||
const player = this.$refs.mediaPlayer
|
||||
if (player && !player.paused) {
|
||||
player.pause()
|
||||
}
|
||||
},
|
||||
startHeartbeatTimer() {
|
||||
this.stopHeartbeatTimer()
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
if (!this.studying) return
|
||||
if (this.isEffectiveLearning()) {
|
||||
this.pendingSeconds += 1
|
||||
}
|
||||
if (this.pendingSeconds >= 15) {
|
||||
this.heartbeat()
|
||||
}
|
||||
}, 1000)
|
||||
},
|
||||
stopHeartbeatTimer() {
|
||||
if (this.heartbeatTimer) {
|
||||
clearInterval(this.heartbeatTimer)
|
||||
this.heartbeatTimer = null
|
||||
}
|
||||
},
|
||||
isEffectiveLearning() {
|
||||
if (document.hidden) return false
|
||||
if (Date.now() - this.lastActiveAt > this.inactiveLimit) return false
|
||||
if (["video", "audio"].includes(this.selectedResource.resourceType)) {
|
||||
const player = this.$refs.mediaPlayer
|
||||
return !!player && !player.paused && !player.ended
|
||||
}
|
||||
return true
|
||||
},
|
||||
markActive() {
|
||||
this.lastActiveAt = Date.now()
|
||||
},
|
||||
applyRecordState(data) {
|
||||
this.recordProgress = Number(data.progressPercent || 0)
|
||||
this.recordStudyTime = data.studyTimeText || this.formatSeconds(Number(data.studySeconds || 0))
|
||||
if (this.selectedResource.id && data.lastPositionSeconds !== undefined) {
|
||||
this.$set(this.selectedResource, "lastPositionSeconds", Number(data.lastPositionSeconds || 0))
|
||||
}
|
||||
},
|
||||
getMediaPosition() {
|
||||
if (!["video", "audio"].includes(this.selectedResource.resourceType)) return 0
|
||||
const player = this.$refs.mediaPlayer
|
||||
return player ? Math.floor(player.currentTime || 0) : Number(this.selectedResource.lastPositionSeconds || 0)
|
||||
},
|
||||
formatSeconds(seconds) {
|
||||
const hour = Math.floor(seconds / 3600)
|
||||
const minute = Math.floor(seconds % 3600 / 60)
|
||||
const second = Math.floor(seconds % 60)
|
||||
if (hour > 0) return hour + "小时" + minute + "分" + second + "秒"
|
||||
if (minute > 0) return minute + "分" + second + "秒"
|
||||
return second + "秒"
|
||||
},
|
||||
splitFlags(flags) {
|
||||
return flags ? flags.split(",").filter(Boolean) : []
|
||||
},
|
||||
getRecommendName(code) {
|
||||
const item = this.recommendOptions.find(v => v.code === code)
|
||||
return item ? item.name : code
|
||||
},
|
||||
openFile() {
|
||||
if (this.fileUrl) window.open(this.fileUrl)
|
||||
},
|
||||
collapseAll() {
|
||||
const nodesMap = this.$refs.studyTree && this.$refs.studyTree.store.nodesMap
|
||||
Object.keys(nodesMap || {}).forEach(key => {
|
||||
nodesMap[key].expanded = false
|
||||
})
|
||||
},
|
||||
async goBack() {
|
||||
this.saveServerPosition(this.getMediaPosition())
|
||||
await this.finishStudy(false, { silent: true })
|
||||
window.location.href = this.apiBase
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.courseId = this.getQuery("id")
|
||||
window.addEventListener("mousemove", this.markActive)
|
||||
window.addEventListener("keydown", this.markActive)
|
||||
window.addEventListener("click", this.markActive)
|
||||
window.addEventListener("scroll", this.markActive, true)
|
||||
window.addEventListener("beforeunload", () => this.finishStudy(true))
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) {
|
||||
this.saveServerPosition(this.getMediaPosition(), true)
|
||||
}
|
||||
})
|
||||
await this.loadRecommendOptions()
|
||||
await this.loadCourse()
|
||||
await this.loadTree()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.finishStudy(true)
|
||||
this.stopHeartbeatTimer()
|
||||
window.removeEventListener("mousemove", this.markActive)
|
||||
window.removeEventListener("keydown", this.markActive)
|
||||
window.removeEventListener("click", this.markActive)
|
||||
window.removeEventListener("scroll", this.markActive, true)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,251 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<style>
|
||||
[v-cloak] { display: none; }
|
||||
#sidebar-menu, #menu-toggle-btn, #menu-overlay { display: none !important; }
|
||||
#sub-app-container-main-content { height: calc(100vh - 64px); padding: 0 !important; overflow: hidden; background: #f6f8fb; }
|
||||
#sub-app-container-main-content-body { height: 100%; overflow: hidden; }
|
||||
.study-v2 { height: 100%; min-height: 0; display: flex; flex-direction: column; overflow: hidden; color: #17233d; background: #f6f8fb; }
|
||||
.study-topbar { height: 56px; flex: none; background: #fff; border-bottom: 1px solid #e7eaf0; }
|
||||
.study-topbar-inner { width: 1124px; height: 100%; margin: 0 auto; display: flex; align-items: center; }
|
||||
.back-link { color: #65728a; cursor: pointer; font-size: 13px; }
|
||||
.breadcrumb-course { margin-left: 16px; padding-left: 16px; border-left: 1px solid #dce1e8; font-size: 13px; }
|
||||
.breadcrumb-course strong { color: #111d34; }
|
||||
.top-progress { width: 166px; margin-left: auto; display: flex; align-items: center; gap: 9px; color: #56637a; font-size: 12px; }
|
||||
.top-progress .el-progress { flex: 1; }
|
||||
.top-progress-value { color: #1769ff; }
|
||||
.study-layout { width: 1124px; min-height: 0; flex: 1; margin: 0 auto; padding: 18px 0; display: grid; grid-template-columns: 230px 875px; gap: 19px; overflow: hidden; box-sizing: border-box; }
|
||||
.study-catalog { max-height: 100%; align-self: start; display: flex; flex-direction: column; overflow: hidden; background: #fff; border: 1px solid #e5e8ed; border-radius: 10px; box-sizing: border-box; }
|
||||
.catalog-head { height: 52px; padding: 0 15px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid #edf0f4; }
|
||||
.catalog-title { font-size: 15px; font-weight: 700; }
|
||||
.catalog-summary { color: #8793a7; font-size: 11px; }
|
||||
.catalog-tree { max-height: calc(100vh - 190px); overflow-x: hidden; overflow-y: auto; padding: 0 0 8px; }
|
||||
.catalog-node { width: 100%; min-width: 0; height: 32px; display: inline-flex; align-items: center; gap: 8px; padding: 0 14px; box-sizing: border-box; }
|
||||
.catalog-node-title { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: #344258; font-size: 13px; }
|
||||
.catalog-outline-node .catalog-node-title { color: #202d43; font-weight: 400; }
|
||||
.catalog-resource-node { padding-left: 15px; }
|
||||
.catalog-resource-node .catalog-node-title { font-weight: 400; }
|
||||
.catalog-node-time { flex: none; color: #8e9bb0; font-size: 11px; }
|
||||
.catalog-status-complete { flex: none; color: #2f80ed; font-size: 15px; }
|
||||
.catalog-status-radio { flex: none; margin-right: 0; line-height: 1; }
|
||||
.catalog-status-radio .el-radio__inner { width: 15px; height: 15px; border-color: #cbd4e1; }
|
||||
.catalog-status-radio .el-radio__input.is-checked .el-radio__inner { border-color: #2f80ed; background: #fff; }
|
||||
.catalog-status-radio .el-radio__input.is-checked .el-radio__inner::after { width: 5px; height: 5px; background: #2f80ed; }
|
||||
.catalog-status-radio .el-radio__label { display: none; }
|
||||
.catalog-toggle { width: 22px; height: 22px; flex: none; padding: 0; display: inline-flex; align-items: center; justify-content: center; color: #96a3b6; border: 0; border-radius: 4px; background: transparent; cursor: pointer; }
|
||||
.catalog-toggle:hover { color: #2f80ed; background: #eef5ff; }
|
||||
.catalog-toggle i { font-size: 12px; }
|
||||
.study-catalog .el-tree-node__content { height: 32px; padding-left: 0 !important; }
|
||||
.study-catalog .el-tree-node__expand-icon { display: none; }
|
||||
.study-catalog .el-tree-node__content:hover { background: #f1f6ff; }
|
||||
.study-catalog .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content { background: #eaf2ff; }
|
||||
.study-catalog .el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content .catalog-node-title { color: #1769ff; }
|
||||
.catalog-node-current { color: #1769ff; background: #eaf2ff; }
|
||||
.catalog-node-current .catalog-node-title { color: #1769ff; }
|
||||
/* 桌面端仅允许右侧课程学习内容滚动,顶部导航和左侧课程清单始终保持固定。 */
|
||||
.study-main { min-width: 0; min-height: 0; height: 100%; overflow-x: hidden; overflow-y: auto; box-sizing: border-box; }
|
||||
.study-main::-webkit-scrollbar { width: 6px; }
|
||||
.study-main::-webkit-scrollbar-thumb { background: #c8d2df; border-radius: 6px; }
|
||||
.study-main::-webkit-scrollbar-track { background: transparent; }
|
||||
.player-card { height: 345px; display: flex; align-items: center; justify-content: center; overflow: hidden; background: #101010; }
|
||||
.media-player { width: 100%; height: 345px; display: block; background: #000; object-fit: contain; }
|
||||
.audio-panel { width: 100%; height: 345px; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #dfe9f5; background: #142c46; }
|
||||
.audio-panel i { margin-bottom: 24px; font-size: 52px; }
|
||||
.audio-panel audio { width: 80%; }
|
||||
.image-preview { max-width: 100%; max-height: 345px; }
|
||||
.doc-frame { width: 100%; height: 345px; border: 0; background: #fff; }
|
||||
.file-fallback { width: 100%; padding: 40px; color: #65748a; text-align: center; background: #fff; box-sizing: border-box; }
|
||||
.empty-resource { color: #9aa6b7; text-align: center; }
|
||||
.empty-resource i { display: block; margin-bottom: 12px; font-size: 44px; }
|
||||
.course-detail-card { margin-top: 14px; padding: 18px 20px 20px; background: #fff; border: 1px solid #e6e9ee; border-radius: 10px; box-sizing: border-box; }
|
||||
.course-detail-head { display: flex; align-items: flex-start; justify-content: space-between; }
|
||||
.detail-course-title { font-size: 18px; font-weight: 700; }
|
||||
.detail-course-type { margin-left: 6px; color: #7f8ba0; font-size: 13px; font-weight: 400; }
|
||||
.detail-learning-state { margin-top: 5px; color: #5f6d82; font-size: 12px; }
|
||||
.detail-actions { display: flex; gap: 7px; }
|
||||
.detail-actions .el-button { margin: 0; border-radius: 17px; }
|
||||
.course-statistics { margin-top: 12px; padding-top: 12px; display: flex; gap: 16px; color: #8290a6; border-top: 1px solid #edf0f4; font-size: 12px; }
|
||||
.course-description { margin-top: 12px; color: #344258; font-size: 13px; line-height: 22px; }
|
||||
.study-bottom { margin-top: 11px; display: flex; align-items: center; justify-content: space-between; color: #8491a5; font-size: 12px; }
|
||||
.study-bottom .el-button { border-radius: 18px; }
|
||||
.page-loading { min-height: 0; flex: 1; display: flex; align-items: center; justify-content: center; color: #2878e5; font-size: 28px; }
|
||||
@media (max-width: 1180px) { .study-topbar-inner, .study-layout { width: calc(100% - 32px); } .study-layout { grid-template-columns: 230px minmax(0, 1fr); } }
|
||||
@media (max-width: 760px) { #sub-app-container-main-content, #sub-app-container-main-content-body { overflow-y: auto; } .study-v2 { height: auto; min-height: 100%; overflow: visible; } .study-layout { min-height: auto; flex: none; grid-template-columns: 1fr; overflow: visible; } .study-catalog { height: auto; order: 2; } .catalog-tree { max-height: 360px; overflow-y: auto; } .study-main { height: auto; padding-right: 0; overflow: visible; } .player-card, .media-player, .audio-panel, .doc-frame { height: 320px; min-height: 320px; } .breadcrumb-course { display: none; } }
|
||||
</style>
|
||||
|
||||
<div id="app" class="study-v2" v-cloak>
|
||||
<div v-if="pageLoading" class="page-loading"><i class="el-icon-loading"></i></div>
|
||||
<template v-else>
|
||||
<header class="study-topbar">
|
||||
<div class="study-topbar-inner">
|
||||
<span class="back-link" @click="goBack"><i class="el-icon-arrow-left"></i> 返回课程列表</span>
|
||||
<div class="breadcrumb-course"><strong>{{course.courseName || '课程学习'}}</strong><span v-if="selectedResource.title"> / {{selectedResource.title}}</span></div>
|
||||
<div class="top-progress"><span>学习进度</span><el-progress :percentage="courseProgress" :show-text="false" :stroke-width="5"></el-progress><span class="top-progress-value">{{courseProgress}}%</span></div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="study-layout">
|
||||
<aside class="study-catalog">
|
||||
<div class="catalog-head"><span class="catalog-title">课程清单</span><span class="catalog-summary">{{completedCount}}/{{resourceCount}} 已完成</span></div>
|
||||
<div class="catalog-tree">
|
||||
<el-tree ref="studyTree" :data="treeData" node-key="id" default-expand-all highlight-current :expand-on-click-node="false" :props="{children:'children', label:'title'}" @node-click="nodeClick">
|
||||
<span slot-scope="{ node, data }" class="catalog-node" :class="{'catalog-outline-node': data.type === 'outline', 'catalog-resource-node': data.type === 'resource', 'catalog-node-current': data.type === 'resource' && data.id === selectedResource.id}">
|
||||
<template v-if="data.type === 'resource'">
|
||||
<i v-if="isResourceCompleted(data)" class="el-icon-success catalog-status-complete"></i>
|
||||
<el-radio v-else class="catalog-status-radio" :value="selectedResource.id" :label="data.id" @click.native.stop @change="nodeClick(data)"><span></span></el-radio>
|
||||
</template>
|
||||
<span class="catalog-node-title" :title="data.title">{{data.title}}</span>
|
||||
<span v-if="data.type === 'resource'" class="catalog-node-time">{{formatShortDuration(data.durationSeconds)}}</span>
|
||||
<button v-else-if="node.childNodes && node.childNodes.length" type="button" class="catalog-toggle" :aria-label="node.expanded ? '收起' + data.title : '展开' + data.title" @click.stop="toggleCatalogNode(node)">
|
||||
<i :class="node.expanded ? 'el-icon-arrow-up' : 'el-icon-arrow-down'"></i>
|
||||
</button>
|
||||
</span>
|
||||
</el-tree>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<section class="study-main">
|
||||
<div class="player-card">
|
||||
<template v-if="selectedResource.id">
|
||||
<video v-if="selectedResource.resourceType === 'video'" ref="mediaPlayer" class="media-player" :src="mediaUrl" controls controlslist="nodownload" @play="mediaPlay" @ended="mediaEnded" @timeupdate="saveProgress" @loadedmetadata="mediaReady" @seeking="handleMediaSeeking" @seeked="handleMediaSeeked"></video>
|
||||
<div v-else-if="selectedResource.resourceType === 'audio'" class="audio-panel"><i class="el-icon-headset"></i><div class="mb20">{{selectedResource.title}}</div><audio ref="mediaPlayer" :src="fileUrl" controls controlslist="nodownload" @play="mediaPlay" @ended="mediaEnded" @timeupdate="saveProgress" @loadedmetadata="mediaReady" @seeking="handleMediaSeeking" @seeked="handleMediaSeeked"></audio></div>
|
||||
<img v-else-if="selectedResource.resourceType === 'image'" class="image-preview" :src="fileUrl" :alt="selectedResource.title">
|
||||
<iframe v-else-if="canInlinePreview(selectedResource)" class="doc-frame" :src="inlinePreviewUrl"></iframe>
|
||||
<div v-else class="file-fallback"><file-preview :files="selectedResource.fileData" complete_result></file-preview><el-button v-if="fileUrl" class="mt20" type="primary" icon="el-icon-view" @click="openFile">打开学习资料</el-button></div>
|
||||
</template>
|
||||
<div v-else class="empty-resource"><i class="el-icon-reading"></i><div>请从左侧目录选择需要学习的资料</div></div>
|
||||
</div>
|
||||
|
||||
<div class="course-detail-card">
|
||||
<div class="course-detail-head">
|
||||
<div><div><span class="detail-course-title">{{course.courseName}}</span><span class="detail-course-type">{{course.courseTypeName || '课程'}}</span></div><div class="detail-learning-state">正在学习:{{selectedResource.title || '请选择课节'}}</div></div>
|
||||
<div class="detail-actions">
|
||||
<el-button v-if="fileUrl" size="mini" icon="el-icon-share" @click="openFile">打开资料</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="course-statistics"><span><i class="el-icon-collection"></i> 共 {{resourceCount}} 课节</span><span><i class="el-icon-time"></i> 总时长 {{formatCourseDuration(course.totalDurationSeconds)}}</span><span><i class="el-icon-view"></i> {{course.viewCount || 0}} 次观看</span></div>
|
||||
<div class="course-description">{{course.courseIntro || '暂无课程介绍'}}</div>
|
||||
</div>
|
||||
<div class="study-bottom">
|
||||
<span>{{studying ? '正在记录有效学习时长:' + recordStudyTime : '播放课程后,系统将自动记录有效学习时长。'}}</span>
|
||||
<div>
|
||||
<el-button v-if="!studying && selectedResource.id && !isMediaResource" size="small" type="primary" plain :loading="startingStudy" @click="startStudy">开始学习</el-button>
|
||||
<el-button v-if="studying" size="small" type="danger" plain @click="finishStudy(false)">结束学习</el-button>
|
||||
<el-button v-if="hasNextResource" size="small" type="primary" @click="nextResource">下一节 <i class="el-icon-arrow-right"></i></el-button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
components: {"file-preview": httpVueLoader("/components/plugins/sysFilePreview/index.vue")},
|
||||
data() {
|
||||
return { courseId: "", preferredResourceId: "", apiBase: "/platform/learning/course/display", recordApi: "/platform/learning/study/record", course: {}, treeData: [], selectedResource: {}, studying: false, startingStudy: false, currentSegmentId: "", recordProgress: 0, recordStudyTime: "0秒", pendingSeconds: 0, heartbeatTimer: null, autoResumeKey: "", lastPositionSyncAt: 0, lastActiveAt: Date.now(), inactiveLimit: 60000, lastAllowedPositionSeconds: 0, restoringMediaPosition: false, pageLoading: true, loadingCount: 0 }
|
||||
},
|
||||
computed: {
|
||||
fileUrl() { return this.getFileUrl(this.selectedResource.fileData) },
|
||||
mediaUrl() {
|
||||
if (this.selectedResource.resourceType !== "video") return this.fileUrl
|
||||
const fileId = this.getFileId(this.selectedResource.fileData)
|
||||
// 视频续播和手动拖动依赖 Range 请求,普通文件地址会导致 currentTime 定位被浏览器重置。
|
||||
return fileId ? "/platform/sys/file/videoPlay?id=" + encodeURIComponent(fileId) : this.fileUrl
|
||||
},
|
||||
inlinePreviewUrl() { const id = this.getFileId(this.selectedResource.fileData); return id ? this.apiBase + "/pdfPreview?id=" + encodeURIComponent(id) : this.fileUrl },
|
||||
resourceTypeText() { const names = {video: "视频资料", audio: "音频资料", image: "图片资料", pdf: "文档资料", word: "文档资料", ppt: "演示资料"}; return names[this.selectedResource.resourceType] || "课程资料" },
|
||||
resourceList() { return this.collectResources(this.treeData) },
|
||||
resourceCount() { return this.resourceList.length },
|
||||
completedCount() { return this.resourceList.filter((item) => item.completeStatus === "completed" || Number(item.progressPercent || 0) >= 100).length },
|
||||
courseProgress() { if (!this.resourceCount) return 0; return Math.round(this.resourceList.reduce((total, item) => total + Number(item.progressPercent || 0), 0) / this.resourceCount) },
|
||||
currentResourceIndex() { return this.resourceList.findIndex((item) => item.id === this.selectedResource.id) },
|
||||
hasNextResource() { return this.currentResourceIndex >= 0 && this.currentResourceIndex < this.resourceList.length - 1 },
|
||||
isMediaResource() { return ["video", "audio"].includes(this.selectedResource.resourceType) }
|
||||
},
|
||||
methods: {
|
||||
beginLoading() { this.loadingCount += 1; this.pageLoading = true },
|
||||
endLoading() { this.loadingCount -= 1; if (this.loadingCount <= 0) { this.loadingCount = 0; this.pageLoading = false } },
|
||||
getQuery(name) { return new URLSearchParams(window.location.search).get(name) || "" },
|
||||
// 请求参数 id 为课程主键;接口返回课程标题等基础字段,作为学习页顶部信息。
|
||||
loadCourse() { this.beginLoading(); this.$axios.post(this.apiBase + "/courseInfo", {id: this.courseId}).then((res) => { if (res.code === 0) { this.course = res.data || {} } else { this.$message.warning(res.msg) } }).finally(() => { this.endLoading() }) },
|
||||
// 请求参数 courseId 为课程主键;优先选中 URL 中 resourceId 指定的资料,否则选中首个可学习资料。
|
||||
loadTree() { this.beginLoading(); this.$axios.post(this.apiBase + "/studyTree", {courseId: this.courseId}).then((res) => { if (res.code !== 0) { this.$message.warning(res.msg); return } this.treeData = res.data || []; const resources = this.collectResources(this.treeData); const current = resources.find(item => item.id === this.preferredResourceId) || resources[0]; if (current) { this.selectResource(current); this.$nextTick(() => { if (this.$refs.studyTree) this.$refs.studyTree.setCurrentKey(current.id) }) } }).finally(() => { this.endLoading() }) },
|
||||
collectResources(nodes) { let resources = []; (nodes || []).forEach((node) => { if (node.type === "resource") resources.push(node); if (node.children && node.children.length) resources = resources.concat(this.collectResources(node.children)) }); return resources },
|
||||
nodeClick(data) { if (data.type !== "resource") return; if (this.studying && this.selectedResource.id !== data.id) { this.$message.warning("请先结束当前资料的学习"); return } this.selectResource(data) },
|
||||
selectResource(resource) { this.selectedResource = resource; this.recordProgress = Number(resource.progressPercent || 0); this.recordStudyTime = this.formatSeconds(Number(resource.studySeconds || 0)); this.autoResumeKey = ""; this.lastAllowedPositionSeconds = Number(resource.maxPositionSeconds || 0); this.restoringMediaPosition = false },
|
||||
// 参数 data 为课节资源;接口完成状态或进度达到 100% 时,清单显示蓝色完成图标。
|
||||
isResourceCompleted(data) { return data.completeStatus === "completed" || Number(data.progressPercent || 0) >= 100 },
|
||||
// 参数 node 为 Element Tree 章节节点;点击右侧按钮只切换章节展开状态,不触发课节选择。
|
||||
toggleCatalogNode(node) { if (!node || node.isLeaf) return; this.$set(node, "expanded", !node.expanded) },
|
||||
getFileUrl(value) { if (!value) return ""; if (Array.isArray(value)) return value.length ? (value[0].url || value[0].response?.data || value[0].data || "") : ""; if (typeof value === "string" && value.trim().startsWith("[")) { try { const files = JSON.parse(value); return files.length ? (files[0].url || files[0].response?.data || files[0].data || "") : "" } catch (e) { return "" } } return value },
|
||||
getFileId(value) { const url = this.getFileUrl(value); if (!url) return ""; const matched = url.match(/[?&]id=([^&]+)/); if (matched) return decodeURIComponent(matched[1]); return !url.includes("/") && !url.includes(".") ? url : "" },
|
||||
canInlinePreview(resource) { const ext = (resource.fileExt || "").toLowerCase(); return ["pdf", "ppt", "word"].includes(resource.resourceType) || ["pdf", "ppt", "pptx", "doc", "docx"].includes(ext) },
|
||||
progressKey() { return "learning-progress-" + this.courseId + "-" + this.selectedResource.id },
|
||||
mediaReady() { this.askResume() },
|
||||
askResume() {
|
||||
if (!["video", "audio"].includes(this.selectedResource.resourceType) || this.studying || this.startingStudy) return
|
||||
const currentKey = this.progressKey()
|
||||
if (this.autoResumeKey === currentKey) return
|
||||
this.autoResumeKey = currentKey
|
||||
const player = this.$refs.mediaPlayer
|
||||
// 仅使用当前登录人的服务端学习记录,加载视频后直接定位但不自动播放。
|
||||
const saved = Math.max(0, Number(this.selectedResource.lastPositionSeconds || 0))
|
||||
if (!player || !saved || saved < 5 || saved >= player.duration - 5) return
|
||||
this.restoreMediaPosition(player, saved, 0)
|
||||
},
|
||||
mediaPlay() { if (!this.studying && !this.startingStudy) this.startStudy({fromMedia: true}) },
|
||||
mediaEnded() { this.finishStudy(false, {silent: true, completed: true}); localStorage.removeItem(this.progressKey()) },
|
||||
playSelectedMedia(seekTo) { if (!["video", "audio"].includes(this.selectedResource.resourceType)) return; this.$nextTick(() => { const player = this.$refs.mediaPlayer; if (!player) return; const play = () => { if (typeof seekTo === "number" && !Number.isNaN(seekTo)) this.restoreMediaPosition(player, seekTo, 0); const playPromise = player.play && player.play(); if (playPromise && playPromise.catch) playPromise.catch(() => { this.$message.warning("浏览器阻止了自动播放,请点击播放器继续") }) }; if (player.readyState >= 1) { play() } else { player.addEventListener("loadedmetadata", play, {once: true}) } }) },
|
||||
// player 为当前视频或音频播放器,positionSeconds 为服务端保存的续播秒数;定位失败时仅重试一次,避免播放器无法拖动时循环请求。
|
||||
restoreMediaPosition(player, positionSeconds, retryCount) {
|
||||
if (!player || positionSeconds <= 0) return
|
||||
let targetPosition = Math.max(0, Math.floor(positionSeconds))
|
||||
if (Number.isFinite(player.duration) && player.duration > 0) {
|
||||
targetPosition = Math.min(targetPosition, Math.max(0, Math.floor(player.duration) - 2))
|
||||
}
|
||||
if (targetPosition <= 0) return
|
||||
this.restoringMediaPosition = true
|
||||
player.addEventListener("seeked", () => {
|
||||
this.lastAllowedPositionSeconds = targetPosition
|
||||
this.restoringMediaPosition = false
|
||||
if (Math.abs(Number(player.currentTime || 0) - targetPosition) <= 1 || retryCount >= 1) return
|
||||
setTimeout(() => this.restoreMediaPosition(player, targetPosition, retryCount + 1), 100)
|
||||
}, {once: true})
|
||||
player.currentTime = targetPosition
|
||||
},
|
||||
// 原生播放器点击进度条会在 seeking 后再次写入时间,因此开始定位和完成定位均需限制向前跳转;回退复习不受影响。
|
||||
restoreBlockedMediaPosition() { const player = this.$refs.mediaPlayer; if (!player || this.selectedResource.allowDrag !== false || this.restoringMediaPosition) return false; const allowedPosition = Math.max(0, Number(this.lastAllowedPositionSeconds || 0)); if (Number(player.currentTime || 0) <= allowedPosition + 0.25) return false; player.currentTime = allowedPosition; setTimeout(() => { if (!this.restoringMediaPosition && this.selectedResource.allowDrag === false && Number(player.currentTime || 0) > allowedPosition + 0.25) player.currentTime = allowedPosition }, 0); return true },
|
||||
handleMediaSeeking() { this.restoreBlockedMediaPosition() },
|
||||
handleMediaSeeked() { this.restoreBlockedMediaPosition() },
|
||||
saveProgress() { if (!["video", "audio"].includes(this.selectedResource.resourceType)) return; const player = this.$refs.mediaPlayer; if (!player || player.currentTime <= 0) return; const position = Math.floor(player.currentTime); if (this.selectedResource.allowDrag === false && !this.restoringMediaPosition && position > Number(this.lastAllowedPositionSeconds || 0) + 2) { this.restoreBlockedMediaPosition(); return } this.lastAllowedPositionSeconds = Math.max(Number(this.lastAllowedPositionSeconds || 0), position); localStorage.setItem(this.progressKey(), String(position)); this.$set(this.selectedResource, "lastPositionSeconds", position); if (Date.now() - this.lastPositionSyncAt > 5000) this.saveServerPosition(position) },
|
||||
// position 为当前播放秒数;页面隐藏或退出时优先使用 Beacon,避免请求被浏览器中断。
|
||||
saveServerPosition(position, useBeacon) { if (!this.selectedResource.id || !["video", "audio"].includes(this.selectedResource.resourceType)) return; const seconds = Math.max(0, Math.floor(Number(position || 0))); this.lastPositionSyncAt = Date.now(); if (useBeacon && navigator.sendBeacon) { const body = new URLSearchParams(); body.append("courseId", this.courseId); body.append("resourceId", this.selectedResource.id); body.append("positionSeconds", String(seconds)); navigator.sendBeacon(this.recordApi + "/position", new Blob([body.toString()], {type: "application/x-www-form-urlencoded;charset=UTF-8"})); return } this.$axios.post(this.recordApi + "/position", {courseId: this.courseId, resourceId: this.selectedResource.id, positionSeconds: seconds}).then(() => {}).finally(() => {}) },
|
||||
// options 支持 autoPlay、seekTo 与 fromMedia;返回的 segmentId 用于后续心跳和结束学习请求。
|
||||
startStudy(options) { const config = options && !options.target ? options : {}; if (!this.selectedResource.id) { this.$message.warning("请选择课程资料"); return } if (this.studying) { if (config.autoPlay) this.playSelectedMedia(config.seekTo); return } this.startingStudy = true; this.$axios.post(this.recordApi + "/start", {courseId: this.courseId, resourceId: this.selectedResource.id, positionSeconds: typeof config.seekTo === "number" ? Math.floor(config.seekTo) : this.getMediaPosition()}).then((res) => { if (res.code !== 0) { this.$message.warning(res.msg); return } this.currentSegmentId = res.data.segmentId; this.studying = true; this.pendingSeconds = 0; this.lastActiveAt = Date.now(); this.$set(this.course, "viewCount", Number(res.data.viewCount || 0)); this.applyRecordState(res.data); this.startHeartbeatTimer(); if (config.autoPlay !== false && !config.fromMedia) this.playSelectedMedia(config.seekTo); this.$message.success("已开始学习") }).finally(() => { this.startingStudy = false }) },
|
||||
heartbeat() { if (!this.studying || !this.currentSegmentId || this.pendingSeconds <= 0) return; const seconds = this.pendingSeconds; this.pendingSeconds = 0; this.$axios.post(this.recordApi + "/heartbeat", {segmentId: this.currentSegmentId, activeSeconds: seconds, positionSeconds: this.getMediaPosition()}).then((res) => { if (res.code === 0) { this.applyRecordState(res.data); return } this.stopHeartbeatTimer(); this.studying = false; this.currentSegmentId = ""; this.$message.warning(res.msg) }).finally(() => {}) },
|
||||
finishStudy(force, options) { const config = options || {}; if (!this.currentSegmentId) return Promise.resolve(); const segmentId = this.currentSegmentId; const seconds = this.pendingSeconds; this.pendingSeconds = 0; this.stopHeartbeatTimer(); this.studying = false; this.currentSegmentId = ""; if (!force) this.pauseSelectedMedia(); if (force && navigator.sendBeacon) { this.saveServerPosition(this.getMediaPosition(), true); const formData = new FormData(); formData.append("segmentId", segmentId); formData.append("activeSeconds", String(seconds)); formData.append("positionSeconds", String(this.getMediaPosition())); navigator.sendBeacon(this.recordApi + "/finish", formData); return Promise.resolve() } return this.$axios.post(this.recordApi + "/finish", {segmentId: segmentId, activeSeconds: seconds, positionSeconds: this.getMediaPosition(), completed: !!config.completed}).then((res) => { if (res.code === 0 && res.data) { this.applyRecordState(res.data); if (!config.silent) this.$message.success("学习已结束") } }).finally(() => {}) },
|
||||
pauseSelectedMedia() { if (!["video", "audio"].includes(this.selectedResource.resourceType)) return; const player = this.$refs.mediaPlayer; if (player && !player.paused) player.pause() },
|
||||
startHeartbeatTimer() { this.stopHeartbeatTimer(); this.heartbeatTimer = setInterval(() => { if (!this.studying) return; if (this.isEffectiveLearning()) this.pendingSeconds += 1; if (this.pendingSeconds >= 15) this.heartbeat() }, 1000) },
|
||||
stopHeartbeatTimer() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null } },
|
||||
isEffectiveLearning() { if (document.hidden || Date.now() - this.lastActiveAt > this.inactiveLimit) return false; if (["video", "audio"].includes(this.selectedResource.resourceType)) { const player = this.$refs.mediaPlayer; return !!player && !player.paused && !player.ended } return true },
|
||||
markActive() { this.lastActiveAt = Date.now() },
|
||||
applyRecordState(data) { this.recordProgress = Number(data.progressPercent || 0); this.recordStudyTime = data.studyTimeText || this.formatSeconds(Number(data.studySeconds || 0)); if (this.selectedResource.id && data.lastPositionSeconds !== undefined) this.$set(this.selectedResource, "lastPositionSeconds", Number(data.lastPositionSeconds || 0)); if (this.selectedResource.id && data.maxPositionSeconds !== undefined) { const maxPositionSeconds = Number(data.maxPositionSeconds || 0); this.$set(this.selectedResource, "maxPositionSeconds", maxPositionSeconds); this.lastAllowedPositionSeconds = Math.max(Number(this.lastAllowedPositionSeconds || 0), maxPositionSeconds) } const resource = this.resourceList.find(item => item.id === this.selectedResource.id); if (resource) { this.$set(resource, "progressPercent", this.recordProgress); this.$set(resource, "studySeconds", Number(data.studySeconds || 0)); this.$set(resource, "completeStatus", data.completeStatus || "studying"); if (data.maxPositionSeconds !== undefined) this.$set(resource, "maxPositionSeconds", Number(data.maxPositionSeconds || 0)) } },
|
||||
getMediaPosition() { if (!["video", "audio"].includes(this.selectedResource.resourceType)) return 0; const player = this.$refs.mediaPlayer; return player ? Math.floor(player.currentTime || 0) : Number(this.selectedResource.lastPositionSeconds || 0) },
|
||||
formatSeconds(seconds) { const hour = Math.floor(seconds / 3600); const minute = Math.floor(seconds % 3600 / 60); const second = Math.floor(seconds % 60); if (hour > 0) return hour + "小时" + minute + "分" + second + "秒"; if (minute > 0) return minute + "分" + second + "秒"; return second + "秒" },
|
||||
formatShortDuration(seconds) { const total = Number(seconds) || 0; if (!total) return ""; const minute = Math.floor(total / 60); const second = total % 60; return String(minute).padStart(2, "0") + ":" + String(second).padStart(2, "0") },
|
||||
formatCourseDuration(seconds) { const total = Number(seconds) || 0; if (total < 3600) return Math.max(1, Math.ceil(total / 60)) + "分钟"; const hour = Math.floor(total / 3600); const minute = Math.ceil(total % 3600 / 60); return minute ? hour + "小时" + minute + "分钟" : hour + "小时" },
|
||||
nextResource() { if (!this.hasNextResource) return; const next = this.resourceList[this.currentResourceIndex + 1]; const changeResource = () => { this.selectResource(next); this.$nextTick(() => { if (this.$refs.studyTree) this.$refs.studyTree.setCurrentKey(next.id) }) }; if (this.studying) { this.finishStudy(false, {silent: true}).then(changeResource) } else { changeResource() } },
|
||||
openFile() { if (this.fileUrl) window.open(this.fileUrl) },
|
||||
collapseAll() { const nodesMap = this.$refs.studyTree && this.$refs.studyTree.store.nodesMap; Object.keys(nodesMap || {}).forEach(key => { nodesMap[key].expanded = false }) },
|
||||
goBack() { this.saveServerPosition(this.getMediaPosition()); this.finishStudy(false, {silent: true}).then(() => { window.location.href = this.apiBase }) }
|
||||
},
|
||||
created() { this.courseId = this.getQuery("id"); this.preferredResourceId = this.getQuery("resourceId"); if (!this.courseId) { this.pageLoading = false; this.$message.warning("请选择课程"); return } window.addEventListener("mousemove", this.markActive); window.addEventListener("keydown", this.markActive); window.addEventListener("click", this.markActive); window.addEventListener("scroll", this.markActive, true); window.addEventListener("beforeunload", () => { this.finishStudy(true) }); document.addEventListener("visibilitychange", () => { if (document.hidden) this.saveServerPosition(this.getMediaPosition(), true) }); this.loadCourse(); this.loadTree() },
|
||||
beforeDestroy() { this.finishStudy(true); this.stopHeartbeatTimer(); window.removeEventListener("mousemove", this.markActive); window.removeEventListener("keydown", this.markActive); window.removeEventListener("click", this.markActive); window.removeEventListener("scroll", this.markActive, true) }
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,412 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="课程名称">
|
||||
<el-input v-model="pageForm.courseName" clearable placeholder="请输入课程名称" style="width: 100%" @keyup.enter.native="doSearch"></el-input>
|
||||
</search-item>
|
||||
<search-item label="课程类型">
|
||||
<el-select v-model="pageForm.courseTypeId" clearable filterable placeholder="请选择课程类型" style="width: 100%">
|
||||
<el-option v-for="item in courseTypeOptions" :key="item.id" :label="item.typeName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="开课时间">
|
||||
<el-date-picker
|
||||
v-model="pageForm.courseTime"
|
||||
type="daterange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
range-separator="至"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</search-item>
|
||||
<search-item label="授课讲师">
|
||||
<el-input v-model="pageForm.lecturerName" clearable placeholder="请输入授课讲师" style="width: 100%" @keyup.enter.native="doSearch"></el-input>
|
||||
</search-item>
|
||||
<search-item label="课程状态">
|
||||
<el-select v-model="pageForm.status" clearable placeholder="请选择课程状态" style="width: 100%">
|
||||
<el-option v-for="item in statusOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="推荐标识">
|
||||
<el-select v-model="pageForm.recommendFlag" clearable placeholder="请选择推荐标识" style="width: 100%">
|
||||
<el-option v-for="item in recommendOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="课程列表">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="el-icon-plus"></i>
|
||||
新增课程
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="80"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程名称" prop="courseName" sortable="custom" show-overflow-tooltip min-width="180"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程类型" prop="courseTypeName" sortable="custom" show-overflow-tooltip width="140"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="授课讲师" prop="lecturerName" sortable="custom" show-overflow-tooltip width="140"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="开课时间" prop="startTime" sortable="custom" width="230">
|
||||
<template slot-scope="{row}">
|
||||
<span v-if="row.openType === 'long_term'">长期开放</span>
|
||||
<span v-else>{{formatTime(row.startTime)}} 至 {{formatTime(row.endTime)}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程状态" prop="status" sortable="custom" width="120">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="getStatusType(row.status)" size="mini">{{getStatusName(row.status)}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="推荐标识" prop="recommendFlags" sortable="custom" show-overflow-tooltip min-width="140">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag
|
||||
v-for="code in splitValue(row.recommendFlags)"
|
||||
:key="code"
|
||||
:type="getRecommendType(code)"
|
||||
size="mini"
|
||||
style="margin: 2px 3px">
|
||||
{{getDictName(code, recommendOptions)}}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="观看次数" prop="viewCount" sortable="custom" width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="排序编码" prop="sortNum" sortable="custom" width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" width="260">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
|
||||
<el-button @click="goChapterContent(row)" size="mini" type="success">章节内容</el-button>
|
||||
<el-button @click="doDelete(row)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog :title="title" :visible.sync="dialogVisible" :close-on-click-modal="false" width="72%">
|
||||
<el-form :model="formData" ref="courseForm" :rules="formRules" label-width="110px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程名称" prop="courseName">
|
||||
<el-input v-model="formData.courseName" maxlength="100" placeholder="请输入课程名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程类型" prop="courseTypeId">
|
||||
<el-select v-model="formData.courseTypeId" filterable placeholder="请选择课程类型" style="width: 100%">
|
||||
<el-option v-for="item in courseTypeOptions" :key="item.id" :label="item.typeName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="授课讲师" prop="lecturerName">
|
||||
<el-input v-model="formData.lecturerName" maxlength="100" placeholder="请输入教师姓名"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="课程状态" prop="status">
|
||||
<el-select v-model="formData.status" placeholder="请选择课程状态" style="width: 100%">
|
||||
<el-option v-for="item in statusOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开课方式" prop="openType">
|
||||
<el-radio-group v-model="formData.openType">
|
||||
<el-radio label="fixed">固定开课时间</el-radio>
|
||||
<el-radio label="long_term">长期开放</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="开课时间" prop="openTime" v-if="formData.openType !== 'long_term'">
|
||||
<el-date-picker
|
||||
v-model="formData.openTime"
|
||||
type="datetimerange"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
range-separator="至"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
style="width: 100%">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="推荐标识" prop="recommendFlagList">
|
||||
<el-select v-model="formData.recommendFlagList" multiple clearable placeholder="请选择推荐标识" style="width: 100%">
|
||||
<el-option v-for="item in recommendOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="排序编码" prop="sortNum">
|
||||
<el-input-number v-model="formData.sortNum" :controls="false" :min="0" :precision="0" style="width: 100%" placeholder="请输入排序编码"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="观看次数" prop="viewCount">
|
||||
<el-input-number v-model="formData.viewCount" :controls="false" :min="0" :precision="0" style="width: 100%" placeholder="请输入观看次数"></el-input-number>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="课程介绍" prop="courseIntro">
|
||||
<el-input v-model="formData.courseIntro" type="textarea" maxlength="2000" :rows="4" show-word-limit placeholder="请输入课程简介"></el-input>
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="适合人群" prop="suitablePeople">
|
||||
<el-input v-model="formData.suitablePeople" type="textarea" maxlength="500" :rows="3" show-word-limit placeholder="请输入适合人群"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="学习目标" prop="learningGoal">
|
||||
<el-input v-model="formData.learningGoal" type="textarea" maxlength="500" :rows="3" show-word-limit placeholder="请输入学习目标"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-form-item label="课程封面" prop="cover">
|
||||
<file-upload
|
||||
:upload_number="1"
|
||||
:upload_size="5242880"
|
||||
:value.sync="formData.cover"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
complete_result
|
||||
upload_mode="image"
|
||||
upload_result_category="interval">
|
||||
</file-upload>
|
||||
<div class="el-upload__tip">只能上传1个文件;只能上传.jpg,.jpeg,.png文件;单个文件大小不能超过5M。</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="subDis" @click="operation">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
title: "",
|
||||
dialogVisible: false,
|
||||
subDis: false,
|
||||
courseTypeOptions: [],
|
||||
recommendOptions: [],
|
||||
statusOptions: [
|
||||
{ name: "草稿", code: "draft" },
|
||||
{ name: "待发布", code: "pending" },
|
||||
{ name: "已发布", code: "published" },
|
||||
{ name: "已下架", code: "offline" }
|
||||
],
|
||||
formData: {},
|
||||
formRules: {
|
||||
courseName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
courseTypeId: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
lecturerName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
status: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
sortNum: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
viewCount: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
cover: [{ required: true, message: "请上传课程封面", trigger: ["blur", "change"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buildPageParams() {
|
||||
const params = Object.assign({}, this.pageForm)
|
||||
params.startTime = params.courseTime && params.courseTime.length ? params.courseTime[0] : ""
|
||||
params.endTime = params.courseTime && params.courseTime.length ? params.courseTime[1] : ""
|
||||
return params
|
||||
},
|
||||
pageData(data = null) {
|
||||
const address = this.pageDataUrl ? this.pageDataUrl : loc() + "/pageData"
|
||||
this.tableLoading = true
|
||||
this.$axios.post(address, data ? data : this.buildPageParams()).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
}).finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
resetSearch() {
|
||||
this.pageForm.courseName = ""
|
||||
this.pageForm.courseTypeId = ""
|
||||
this.pageForm.courseTime = []
|
||||
this.pageForm.lecturerName = ""
|
||||
this.pageForm.status = ""
|
||||
this.pageForm.recommendFlag = ""
|
||||
this.doSearch()
|
||||
},
|
||||
openAdd() {
|
||||
this.title = "新增课程"
|
||||
this.formData = {
|
||||
courseName: "",
|
||||
courseTypeId: "",
|
||||
lecturerName: "",
|
||||
courseIntro: "",
|
||||
suitablePeople: "",
|
||||
learningGoal: "",
|
||||
openType: "fixed",
|
||||
openTime: [],
|
||||
status: "draft",
|
||||
recommendFlagList: [],
|
||||
sortNum: 0,
|
||||
viewCount: 0,
|
||||
cover: ""
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.courseForm && this.$refs.courseForm.clearValidate()
|
||||
})
|
||||
},
|
||||
openEdit(row) {
|
||||
this.title = "编辑课程"
|
||||
this.formData = Object.assign({}, row, {
|
||||
openTime: row.startTime && row.endTime ? [this.formatTime(row.startTime), this.formatTime(row.endTime)] : [],
|
||||
recommendFlagList: this.splitValue(row.recommendFlags),
|
||||
viewCount: row.viewCount == null ? 0 : row.viewCount
|
||||
})
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.courseForm && this.$refs.courseForm.clearValidate()
|
||||
})
|
||||
},
|
||||
goChapterContent(row) {
|
||||
const url = "/platform/learning/chapter/content?courseId=" + encodeURIComponent(row.id || "")
|
||||
if (window.commonUtil && commonUtil.pjaxPush) {
|
||||
commonUtil.pjaxPush(url)
|
||||
} else {
|
||||
window.location.href = url
|
||||
}
|
||||
},
|
||||
operation() {
|
||||
this.$refs.courseForm.validate((valid) => {
|
||||
if (!valid) return
|
||||
if (this.formData.openType !== "long_term" && (!this.formData.openTime || this.formData.openTime.length !== 2)) {
|
||||
this.$message.warning("请选择开课时间")
|
||||
return
|
||||
}
|
||||
const data = Object.assign({}, this.formData)
|
||||
data.startTime = data.openType === "long_term" ? "" : data.openTime[0]
|
||||
data.endTime = data.openType === "long_term" ? "" : data.openTime[1]
|
||||
data.recommendFlags = (data.recommendFlagList || []).join(",")
|
||||
delete data.openTime
|
||||
delete data.recommendFlagList
|
||||
const method = data.id ? "/doEdit" : "/doAdd"
|
||||
this.subDis = true
|
||||
this.$axios.post(loc() + method, data).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.dialogVisible = false
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}).finally(() => {
|
||||
this.subDis = false
|
||||
})
|
||||
})
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定要删除该课程吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(() => {
|
||||
this.$axios.post(loc() + "/doDelete", { id: row.id }).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
getCourseTypes() {
|
||||
return this.$axios.post(loc() + "/courseTypes").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.courseTypeOptions = resp.data
|
||||
}
|
||||
})
|
||||
},
|
||||
getLearningDict(name) {
|
||||
return this.$axios.post(loc() + "/learningDictOptions", { name }).then((resp) => {
|
||||
return resp.code === 0 ? resp.data : []
|
||||
})
|
||||
},
|
||||
splitValue(value) {
|
||||
return value ? value.split(",").filter(Boolean) : []
|
||||
},
|
||||
getStatusName(code) {
|
||||
const item = this.statusOptions.find(v => v.code === code)
|
||||
return item ? item.name : ""
|
||||
},
|
||||
getStatusType(code) {
|
||||
const map = {
|
||||
draft: "info",
|
||||
pending: "warning",
|
||||
published: "success",
|
||||
offline: "danger"
|
||||
}
|
||||
return map[code] || ""
|
||||
},
|
||||
getDictName(code, options) {
|
||||
const item = options.find(v => v.code === code)
|
||||
return item ? item.name : code
|
||||
},
|
||||
getDictNames(value, options) {
|
||||
const values = this.splitValue(value)
|
||||
return values.map(code => {
|
||||
const item = options.find(v => v.code === code)
|
||||
return item ? item.name : code
|
||||
}).join("、")
|
||||
},
|
||||
getRecommendType(code) {
|
||||
const name = this.getDictName(code, this.recommendOptions)
|
||||
if (name.includes("热门") || name.includes("推荐")) return "warning"
|
||||
if (name.includes("新")) return "success"
|
||||
return ""
|
||||
},
|
||||
formatTime(time) {
|
||||
return time ? this.$moment(time).format("YYYY-MM-DD HH:mm:ss") : ""
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageForm.courseTime = []
|
||||
Promise.all([this.getCourseTypes(), this.getLearningDict("推荐标识")]).then((values) => {
|
||||
this.recommendOptions = values[1]
|
||||
this.pageData()
|
||||
})
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,189 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="课程类型名称">
|
||||
<el-input
|
||||
v-model="pageForm.typeName"
|
||||
clearable
|
||||
placeholder="请输入课程类型名称"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="课程类型列表">
|
||||
<el-button @click="openAdd" size="medium" type="primary">
|
||||
<i class="el-icon-plus"></i>
|
||||
新建
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border>
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="80"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程类型名称" prop="typeName" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="备注" prop="remark" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="排序编号" prop="sortNum" width="140"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="是否启用" prop="enabled" width="140">
|
||||
<template slot-scope="{row}">
|
||||
<el-switch
|
||||
v-model="row.enabled"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
@change="switchChange(row)">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" width="180">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="openEdit(row)" size="mini" type="primary">编辑</el-button>
|
||||
<el-button @click="doDelete(row)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
|
||||
<el-dialog :title="title" :visible.sync="dialogVisible" :close-on-click-modal="false" width="40%">
|
||||
<el-form :model="formData" ref="courseTypeForm" :rules="formRules" label-width="120px">
|
||||
<el-form-item label="课程类型名称" prop="typeName">
|
||||
<el-input v-model="formData.typeName" maxlength="100" placeholder="请输入课程类型名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="排序编号" prop="sortNum">
|
||||
<el-input-number
|
||||
v-model="formData.sortNum"
|
||||
:controls="false"
|
||||
:min="0"
|
||||
:precision="0"
|
||||
placeholder="请输入排序编号"
|
||||
style="width: 100%">
|
||||
</el-input-number>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否启用" prop="enabled">
|
||||
<el-switch v-model="formData.enabled" active-color="#13ce66" inactive-color="#ff4949"></el-switch>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
v-model="formData.remark"
|
||||
type="textarea"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
:rows="4"
|
||||
placeholder="请输入备注">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="subDis" @click="operation">确定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
title: "",
|
||||
dialogVisible: false,
|
||||
subDis: false,
|
||||
formData: {},
|
||||
formRules: {
|
||||
typeName: [{ required: true, message: "必填", trigger: ["blur", "change"] }],
|
||||
sortNum: [{ required: true, message: "必填", trigger: ["blur", "change"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetSearch() {
|
||||
this.pageForm.typeName = ""
|
||||
this.doSearch()
|
||||
},
|
||||
openAdd() {
|
||||
this.title = "新建课程类型"
|
||||
this.formData = {
|
||||
typeName: "",
|
||||
sortNum: 0,
|
||||
enabled: true,
|
||||
remark: ""
|
||||
}
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.courseTypeForm && this.$refs.courseTypeForm.clearValidate()
|
||||
})
|
||||
},
|
||||
openEdit(row) {
|
||||
this.title = "编辑课程类型"
|
||||
this.formData = Object.assign({}, row)
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.courseTypeForm && this.$refs.courseTypeForm.clearValidate()
|
||||
})
|
||||
},
|
||||
operation() {
|
||||
const method = this.formData.id ? "/doEdit" : "/doAdd"
|
||||
this.$refs.courseTypeForm.validate(async (valid) => {
|
||||
if (!valid) return
|
||||
this.subDis = true
|
||||
const resp = await this.$axios.post(loc() + method, this.formData)
|
||||
this.subDis = false
|
||||
if (resp.code === 0) {
|
||||
this.dialogVisible = false
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
async switchChange(row) {
|
||||
const resp = await this.$axios.post(loc() + "/doEdit", row)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
row.enabled = !row.enabled
|
||||
}
|
||||
},
|
||||
doDelete(row) {
|
||||
this.$confirm("确定要删除该课程类型吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post(loc() + "/doDelete", { id: row.id })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,180 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="工号/姓名">
|
||||
<el-input
|
||||
v-model="pageForm.keyword"
|
||||
clearable
|
||||
placeholder="请输入工号或姓名"
|
||||
style="width: 100%"
|
||||
@keyup.enter.native="doSearch">
|
||||
</el-input>
|
||||
</search-item>
|
||||
<search-item label="所属分工会" v-if="unionOptions.length">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属分工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="课程名称">
|
||||
<el-select v-model="pageForm.courseId" clearable filterable placeholder="请选择课程" style="width: 100%">
|
||||
<el-option v-for="item in courseOptions" :key="item.id" :label="item.courseName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="是否完成">
|
||||
<el-select v-model="pageForm.completeStatus" clearable placeholder="请选择完成状态" style="width: 100%">
|
||||
<el-option v-for="item in statusOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool :app="this" label="学习列表"></table-tool>
|
||||
<el-table
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="70"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="工号" prop="loginName" sortable="custom" show-overflow-tooltip width="120"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="姓名" prop="userName" sortable="custom" show-overflow-tooltip width="110"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="所属分工会" prop="unionName" sortable="custom" show-overflow-tooltip min-width="150"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程名称" prop="courseName" sortable="custom" show-overflow-tooltip min-width="180"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="章节名称" prop="outlineName" sortable="custom" show-overflow-tooltip min-width="160"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学习开始时间" prop="firstStudyTime" sortable="custom" width="170">
|
||||
<template slot-scope="{row}">{{formatTime(row.firstStudyTime)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="最近学习时间" prop="latestStudyTime" sortable="custom" width="170">
|
||||
<template slot-scope="{row}">{{formatTime(row.latestStudyTime)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学习时长" prop="studySeconds" sortable="custom" width="130">
|
||||
<template slot-scope="{row}">{{row.studyTimeText}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学习进度" prop="progressPercent" sortable="custom" width="180">
|
||||
<template slot-scope="{row}">
|
||||
<el-progress :percentage="Number(row.progressPercent || 0)" :stroke-width="8"></el-progress>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="是否完成" prop="completeStatus" sortable="custom" width="110">
|
||||
<template slot-scope="{row}">
|
||||
<el-tag :type="statusType(row.completeStatus)" size="mini">{{statusName(row.completeStatus)}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" width="100">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" type="danger" icon="el-icon-delete" @click="deleteRecord(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
apiBase: "/platform/learning/study/record",
|
||||
courseOptions: [],
|
||||
unionOptions: [],
|
||||
statusOptions: [
|
||||
{ name: "未开始", code: "not_started", type: "info" },
|
||||
{ name: "学习中", code: "studying", type: "warning" },
|
||||
{ name: "已完成", code: "completed", type: "success" }
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
this.$axios.post(this.apiBase + "/pageData", this.pageForm).then(resp => {
|
||||
this.tableLoading = false
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list || []
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}).catch(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
pageOrder(column) {
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
resetSearch() {
|
||||
this.pageForm.keyword = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.courseId = ""
|
||||
this.pageForm.completeStatus = ""
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageData()
|
||||
},
|
||||
async loadOptions() {
|
||||
const courseResp = await this.$axios.post(this.apiBase + "/courseOptions")
|
||||
if (courseResp.code === 0) {
|
||||
this.courseOptions = courseResp.data || []
|
||||
}
|
||||
const unionResp = await this.$axios.post(this.apiBase + "/unionOptions")
|
||||
if (unionResp.code === 0) {
|
||||
this.unionOptions = unionResp.data || []
|
||||
}
|
||||
},
|
||||
statusName(code) {
|
||||
const item = this.statusOptions.find(v => v.code === code)
|
||||
return item ? item.name : "未开始"
|
||||
},
|
||||
statusType(code) {
|
||||
const item = this.statusOptions.find(v => v.code === code)
|
||||
return item ? item.type : "info"
|
||||
},
|
||||
formatTime(value) {
|
||||
return value ? this.$moment(value).format("YYYY-MM-DD HH:mm:ss") : ""
|
||||
},
|
||||
deleteRecord(row) {
|
||||
this.$confirm("确定要删除该学习记录吗?删除后该章节的学习时段也会一并清理。", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
}).then(async () => {
|
||||
const resp = await this.$axios.post(this.apiBase + "/delete", { id: row.id })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.pageData()
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$set(this.pageForm, "keyword", "")
|
||||
this.$set(this.pageForm, "unionId", "")
|
||||
this.$set(this.pageForm, "courseId", "")
|
||||
this.$set(this.pageForm, "completeStatus", "")
|
||||
await this.loadOptions()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,167 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<el-card shadow="never">
|
||||
<search :is-search-button="false">
|
||||
<search-item label="课程名称">
|
||||
<el-select
|
||||
v-model="pageForm.courseId"
|
||||
clearable
|
||||
filterable
|
||||
remote
|
||||
:remote-method="queryCourseOptions"
|
||||
placeholder="请选择课程"
|
||||
style="width: 100%">
|
||||
<el-option v-for="item in courseOptions" :key="item.id" :label="item.courseName" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<search-item label="所属分工会" v-if="unionOptions.length">
|
||||
<el-select v-model="pageForm.unionId" clearable filterable placeholder="请选择所属分工会" style="width: 100%">
|
||||
<el-option v-for="item in unionOptions" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
<div class="search-query">
|
||||
<el-button size="medium" type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
<el-button size="medium" @click="resetSearch">重置</el-button>
|
||||
</div>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<el-tabs v-model="activeTab" @tab-click="tabChange">
|
||||
<el-tab-pane label="课程统计" name="course"></el-tab-pane>
|
||||
<el-tab-pane label="分工会统计" name="union"></el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<table-tool :app="this" :label="activeTab === 'course' ? '课程统计' : '分工会统计'"></table-tool>
|
||||
<el-table
|
||||
v-if="activeTab === 'course'"
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="70"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程名称" prop="courseName" sortable="custom" show-overflow-tooltip min-width="220" ></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学习人数" prop="learnerCount" sortable="custom" width="130"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="完成人数" prop="completedCount" sortable="custom" width="130"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="平均学习时长" prop="avgStudySeconds" sortable="custom" width="170">
|
||||
<template slot-scope="{row}">{{row.avgStudyTimeText}}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-table
|
||||
v-else
|
||||
v-loading="tableLoading"
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
border
|
||||
@sort-change="pageOrder">
|
||||
<el-table-column :index="indexMethod" align="center" header-align="center" label="序号" type="index" width="70"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="课程名称" prop="courseName" sortable="custom" show-overflow-tooltip min-width="220"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="分工会名称" prop="unionName" sortable="custom" show-overflow-tooltip min-width="170"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学习人数" prop="learnerCount" sortable="custom" width="130"></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="学习时长" prop="studySeconds" sortable="custom" width="160">
|
||||
<template slot-scope="{row}">{{row.studyTimeText}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="完成率" prop="completeRate" sortable="custom" width="130"></el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
apiBase: "/platform/learning/statistics",
|
||||
activeTab: "course",
|
||||
courseOptions: [],
|
||||
unionOptions: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
const url = this.activeTab === "course" ? "/coursePageData" : "/unionPageData"
|
||||
this.$axios.post(this.apiBase + url, this.pageForm).then(resp => {
|
||||
this.tableLoading = false
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list || []
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.$message.warning(resp.msg)
|
||||
}
|
||||
}).catch(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
},
|
||||
pageOrder(column) {
|
||||
this.pageForm.pageOrderName = column.prop
|
||||
this.pageForm.pageOrderBy = column.order
|
||||
this.pageData()
|
||||
},
|
||||
async doSearch() {
|
||||
this.pageForm.pageNumber = 1
|
||||
await this.ensureUnionDefaultCourse()
|
||||
this.pageData()
|
||||
},
|
||||
async resetSearch() {
|
||||
this.pageForm.courseId = ""
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.pageOrderName = ""
|
||||
this.pageForm.pageOrderBy = ""
|
||||
this.pageForm.pageNumber = 1
|
||||
await this.queryCourseOptions("")
|
||||
await this.ensureUnionDefaultCourse()
|
||||
this.pageData()
|
||||
},
|
||||
async tabChange() {
|
||||
this.tableData = []
|
||||
this.pageForm.pageNumber = 1
|
||||
this.pageForm.pageOrderName = ""
|
||||
this.pageForm.pageOrderBy = ""
|
||||
await this.ensureUnionDefaultCourse()
|
||||
this.pageData()
|
||||
},
|
||||
async queryCourseOptions(query) {
|
||||
const resp = await this.$axios.post(this.apiBase + "/courseOptions", { courseName: query || "" })
|
||||
if (resp.code === 0) {
|
||||
this.courseOptions = resp.data || []
|
||||
}
|
||||
},
|
||||
async ensureUnionDefaultCourse() {
|
||||
if (this.activeTab !== "union" || this.pageForm.courseId) {
|
||||
return
|
||||
}
|
||||
if (!this.courseOptions.length) {
|
||||
await this.queryCourseOptions("")
|
||||
}
|
||||
if (this.courseOptions.length) {
|
||||
this.pageForm.courseId = this.courseOptions[0].id
|
||||
}
|
||||
},
|
||||
async loadUnionOptions() {
|
||||
const resp = await this.$axios.post(this.apiBase + "/unionOptions")
|
||||
if (resp.code === 0) {
|
||||
this.unionOptions = resp.data || []
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.$set(this.pageForm, "courseId", "")
|
||||
this.$set(this.pageForm, "unionId", "")
|
||||
await this.queryCourseOptions("")
|
||||
await this.loadUnionOptions()
|
||||
this.pageData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,630 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch()">
|
||||
<search-item label="">
|
||||
<el-checkbox-group @change="doSearch" v-model="pageForm.isMenWomen">
|
||||
<el-checkbox-button :key="2" :label="2">男子</el-checkbox-button>
|
||||
<el-checkbox-button :key="3" :label="3">女子</el-checkbox-button>
|
||||
<el-checkbox-button :key="4" :label="4">个人</el-checkbox-button>
|
||||
<el-checkbox-button :key="5" :label="5">团体</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</search-item>
|
||||
|
||||
<search-item label="比赛组别">
|
||||
<el-select @focus="focusGroup" clearable placeholder="请选择比赛组别"
|
||||
style="width: 100%" v-model="pageForm.group">
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id"
|
||||
v-for="item in groupList"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
|
||||
<search-item label="运动类型">
|
||||
<el-select
|
||||
@focus="exerciseType"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择运动类型"
|
||||
style="width: 100%"
|
||||
v-model="pageForm.events"
|
||||
>
|
||||
<el-option :key="item.id" :label="item.name" :value="item.id"
|
||||
v-for="item in exerciseTypeList"></el-option>
|
||||
</el-select>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool label="比赛项目列表">
|
||||
<el-input @input="ageInput" placeholder="输入年龄算出身年" size="small"
|
||||
style="width: auto" type="number" v-model="pageForm.age">
|
||||
<template slot="prepend">年龄计算器</template>
|
||||
</el-input>
|
||||
<el-tag class="ml10">{{ pageForm.ageYear }}</el-tag>
|
||||
<el-date-picker
|
||||
class="ml10"
|
||||
end-placeholder="结束日期"
|
||||
format="yyyy 年 MM 月 dd 日"
|
||||
range-separator="-"
|
||||
size="small"
|
||||
start-placeholder="开始日期"
|
||||
type="daterange"
|
||||
v-model="pageForm.ageDate"
|
||||
value-format="yyyy-MM-dd"
|
||||
></el-date-picker>
|
||||
<el-button @click="doAssignmentDate" class="ml10" icon="el-icon-check"
|
||||
size="small" type="primary">统赋年龄限制
|
||||
</el-button>
|
||||
<el-button @click="openAdd" icon="el-icon-edit-outline" size="small"
|
||||
type="primary">创建比赛项目
|
||||
</el-button>
|
||||
</table-tool>
|
||||
|
||||
<el-table
|
||||
:data="tableData"
|
||||
:size="tableSize"
|
||||
@selection-change="handleSelectionChange"
|
||||
@sort-change="pageOrder"
|
||||
class="vi-table"
|
||||
ref="multipleTable"
|
||||
row-key="id"
|
||||
style="width: 100%; margin-bottom: 20px"
|
||||
v-loading="tableLoading"
|
||||
>
|
||||
<el-table-column :reserve-selection="true" type="selection"
|
||||
width="55"></el-table-column>
|
||||
<el-table-column
|
||||
:index="indexMethod"
|
||||
align="center"
|
||||
header-align="center"
|
||||
label="序号"
|
||||
type="index"
|
||||
width="80px"
|
||||
></el-table-column>
|
||||
<el-table-column
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
:width="column.width"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns"
|
||||
>
|
||||
<template scope="{row}" v-if="column.prop=='baname'">
|
||||
<span>{{row.baname!=null?row.baname:'暂无'}}</span>
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop=='diname'">
|
||||
<span>{{row.diname!=null?row.diname:'暂无'}}</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='isMenWomen'">
|
||||
<span v-if="row.isMenWomen==1">男子</span>
|
||||
<span v-if="row.isMenWomen==2">女子</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='spname'">
|
||||
<span>{{row.spname!=null?row.spname:''}}</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='projectType'">
|
||||
<span>{{row.projectType=='1'?'个人':'团体'}}</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='note'">
|
||||
<span>{{row.note!=''?row.note:'暂无'}}</span>
|
||||
</template>
|
||||
|
||||
<template scope="{row}" v-else-if="column.prop=='whetherEnable'">
|
||||
<el-switch
|
||||
@change="switchChange(row)"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949"
|
||||
v-model="row.whetherEnable"
|
||||
></el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center" header-align="center" label="操作"
|
||||
prop="userOnline" width="150px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="openEdit(row)" size="mini" type="primary">编辑
|
||||
</el-button>
|
||||
<el-button @click="doDelete(row)" size="mini" type="danger">删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #edit>
|
||||
<table-tool label="填写比赛项目信息"></table-tool>
|
||||
|
||||
<el-form :model="formData" :rules="formRules" label-suffix=":" label-width="120px"
|
||||
ref="form">
|
||||
<el-row :gutter="40">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="是否趣味项目" prop="isInterest">
|
||||
<el-checkbox v-model="formData.isInterest">
|
||||
{{formData.isInterest?'是趣味':'不是趣味'}}
|
||||
</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="比赛项目编号" prop="projectCode">
|
||||
<el-input placeholder="请填写比赛项目编号"
|
||||
v-model="formData.projectCode"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="性别" prop="isMenWomen">
|
||||
<el-select
|
||||
allow-create
|
||||
clearable
|
||||
default-first-option
|
||||
filterable
|
||||
placeholder="请选择男女分组"
|
||||
style="width: 100%"
|
||||
v-model="formData.isMenWomen"
|
||||
>
|
||||
<el-option :key="item.id" :label="item.name"
|
||||
:value="item.id" v-for="item in menWomenList"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="比赛组别" prop="competitionCategory">
|
||||
<el-select
|
||||
@focus="focusGroup"
|
||||
clearable
|
||||
placeholder="请选择比赛组别"
|
||||
style="width: 100%"
|
||||
v-model="formData.competitionCategory"
|
||||
>
|
||||
<el-option
|
||||
:key="item.id"
|
||||
:label="item.name+'('+item.code+')'"
|
||||
:value="item.id"
|
||||
v-for="item in groupList"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="距离" prop="distance">
|
||||
<el-select @focus="focusDistance" clearable
|
||||
placeholder="请选择距离" style="width: 100%" v-model="formData.distance">
|
||||
<el-option :key="item.id" :label="item.name"
|
||||
:value="item.id" v-for="item in distanceList"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="运动类型" prop="exerciseType">
|
||||
<el-select
|
||||
@change="changeExercise_typeList"
|
||||
@focus="exerciseType"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择运动类型"
|
||||
style="width: 100%"
|
||||
v-model="formData.exerciseType"
|
||||
>
|
||||
<el-option :key="item.id" :label="item.name"
|
||||
:value="item.id" v-for="item in exerciseTypeList"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="运动项目" prop="sports">
|
||||
<el-select clearable placeholder="请选择具体运动项目" style="width: 100%"
|
||||
v-model="formData.sports">
|
||||
<el-option :key="item.id" :label="item.name"
|
||||
:value="item.id" v-for="item in exerciseTypeList2"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="项目类型" prop="projectType">
|
||||
<el-select
|
||||
allow-create
|
||||
clearable
|
||||
default-first-option
|
||||
filterable
|
||||
placeholder="请选择具体运动项目"
|
||||
style="width: 100%"
|
||||
v-model="formData.projectType"
|
||||
>
|
||||
<el-option :key="item.id" :label="item.name"
|
||||
:value="item.id" v-for="item in projectTypeList"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="年龄限制范围" prop="ageDate" v-if="formData.projectType==1">
|
||||
<el-date-picker
|
||||
end-placeholder="结束日期"
|
||||
format="yyyy 年 MM 月 dd 日"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
style="width: 100%"
|
||||
type="daterange"
|
||||
v-model="formData.ageDate"
|
||||
value-format="yyyy-MM-dd"
|
||||
></el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div style="float: right; margin: 20px 0">
|
||||
<el-button @click="formData.id?doEdit():doAdd()" type="primary">提 交</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
age: 1,
|
||||
ageYear: null,
|
||||
activityId: null,
|
||||
ageDate: null,
|
||||
isMenWomen: []
|
||||
},
|
||||
|
||||
groupList: [],
|
||||
exerciseTypeList: [],
|
||||
distanceList: [],
|
||||
exerciseTypeList2: [],
|
||||
|
||||
tableColumns: [
|
||||
{ prop: "projectCode", label: "项目编号", sortable: true },
|
||||
{ prop: "allName", label: "全称", width: "200" },
|
||||
{ prop: "isMenWomen", label: "男子/女子", sortable: true },
|
||||
{ prop: "baname", label: "比赛组别", sortable: true },
|
||||
{ prop: "diname", label: "距离", sortable: true },
|
||||
{ prop: "exename", label: "运动类型", sortable: true },
|
||||
{ prop: "spname", label: "运动项目", sortable: true },
|
||||
{ prop: "projectType", label: "项目类型", sortable: true },
|
||||
{ prop: "whetherEnable", label: "是否启用" }
|
||||
],
|
||||
|
||||
multipleSelection: [],
|
||||
|
||||
projectTypeList: [
|
||||
{ id: "1", name: "个人" },
|
||||
{ id: "2", name: "团体" }
|
||||
],
|
||||
menWomenList: [
|
||||
{ id: 1, name: "男子" },
|
||||
{ id: 2, name: "女子" }
|
||||
],
|
||||
|
||||
formRules: {
|
||||
exerciseType: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
|
||||
projectType: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
|
||||
projectCode: [{ required: true, message: "必填", trigger: ["change", "blur"] }]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
doAdd() {
|
||||
this.$refs["form"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
const { data } = await this.$axios.post(loc() + "/getProjectCodeIsExist", { projectCode: this.formData.projectCode })
|
||||
if (data) {
|
||||
this.$message.error("编号已存在")
|
||||
return
|
||||
}
|
||||
let allName = ""
|
||||
this.groupList.find((v) => {
|
||||
if (v.id === this.formData.competitionCategory) {
|
||||
allName += v.name
|
||||
}
|
||||
})
|
||||
|
||||
this.menWomenList.find((v) => {
|
||||
if (v.id === this.formData.isMenWomen) {
|
||||
allName += v.name
|
||||
}
|
||||
})
|
||||
|
||||
this.distanceList.find((v) => {
|
||||
if (v.id === this.formData.distance) {
|
||||
allName += v.name + "米"
|
||||
}
|
||||
})
|
||||
|
||||
/* this.exerciseTypeList.find(v => {
|
||||
if (v.id === this.formData.exerciseType) {
|
||||
allName += v.name
|
||||
}
|
||||
})*/
|
||||
|
||||
this.exerciseTypeList2.find((v) => {
|
||||
if (v.id === this.formData.sports) {
|
||||
allName += v.name
|
||||
}
|
||||
})
|
||||
|
||||
/* this.projectTypeList.find(v => {
|
||||
if (v.id === this.formData.projectType) {
|
||||
allName += "(" + v.name + ")"
|
||||
}
|
||||
})*/
|
||||
|
||||
this.formData.allName = allName
|
||||
if (this.formData.ageDate && this.formData.ageDate.length > 0) {
|
||||
this.formData.startAgeDate = this.formData.ageDate[0]
|
||||
this.formData.endAgeDate = this.formData.ageDate[1]
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
const resp = await this.$axios.post(loc() + "/doAdd", this.formData)
|
||||
this.loading = false
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
this.$refs.guava.index()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
doEdit() {
|
||||
this.$refs["form"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
if (this.formData.oldProjectCode !== this.formData.projectCode) {
|
||||
const { data } = await this.$axios.post(loc() + "/getProjectCodeIsExist", { projectCode: this.formData.projectCode })
|
||||
if (data) {
|
||||
this.$message.error("编号已存在")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let allName = ""
|
||||
|
||||
this.groupList.find((v) => {
|
||||
if (v.id === this.formData.competitionCategory) {
|
||||
allName += v.name
|
||||
}
|
||||
})
|
||||
|
||||
this.menWomenList.find((v) => {
|
||||
if (v.id === this.formData.isMenWomen) {
|
||||
allName += v.name
|
||||
}
|
||||
})
|
||||
|
||||
this.distanceList.find((v) => {
|
||||
if (v.id === this.formData.distance) {
|
||||
allName += v.name + "米"
|
||||
}
|
||||
})
|
||||
|
||||
/* this.exerciseTypeList.find(v => {
|
||||
if (v.id === this.formData.exerciseType) {
|
||||
allName += v.name
|
||||
}
|
||||
})*/
|
||||
|
||||
this.exerciseTypeList2.find((v) => {
|
||||
if (v.id === this.formData.sports) {
|
||||
allName += v.name
|
||||
}
|
||||
})
|
||||
|
||||
/* this.projectTypeList.find(v => {
|
||||
if (v.id === this.formData.projectType) {
|
||||
allName += "(" + v.name + ")"
|
||||
}
|
||||
})*/
|
||||
|
||||
this.formData.allName = allName
|
||||
if (this.formData.ageDate && this.formData.ageDate.length > 0) {
|
||||
this.formData.startAgeDate = this.formData.ageDate[0]
|
||||
this.formData.endAgeDate = this.formData.ageDate[1]
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
const resp = await this.$axios.post(loc() + "/doEdit", this.formData)
|
||||
this.loading = true
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
this.$refs.guava.index()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
openEdit(data) {
|
||||
this.focusGroup()
|
||||
this.exerciseType()
|
||||
this.focusDistance()
|
||||
this.formData = {
|
||||
id: data.id,
|
||||
isInterest: data.isInterest,
|
||||
competitionCategory: data.competitionCategory,
|
||||
distance: data.distance,
|
||||
exerciseType: data.exerciseType,
|
||||
sports: data.sports,
|
||||
projectType: data.projectType,
|
||||
note: data.note,
|
||||
isMenWomen: data.isMenWomen,
|
||||
projectCode: data.projectCode,
|
||||
oldProjectCode: data.projectCode,
|
||||
whetherEnable: data.whetherEnable
|
||||
}
|
||||
if (data.startAgeDate != null) {
|
||||
this.$set(this.formData, "ageDate", [data.startAgeDate, data.endAgeDate])
|
||||
}
|
||||
|
||||
this.changeExercise_typeList()
|
||||
this.$refs.guava.edit()
|
||||
},
|
||||
doDelete(row) {
|
||||
const { id } = row
|
||||
this.$confirm("确定要删除该项目吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: async (a, b) => {
|
||||
if ("confirm" === a) {
|
||||
//确认后再执行
|
||||
this.$set(row, "loading", true)
|
||||
const resp = await this.$axios.post(loc() + "/doDelete", { id: id })
|
||||
this.$set(row, "loading", false)
|
||||
if (resp.code === 0) {
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val
|
||||
},
|
||||
//查询比赛组别
|
||||
async focusGroup() {
|
||||
const resp = await this.$axios.post(loc() + "/focusGroup")
|
||||
if (resp.code === 0) {
|
||||
this.groupList = resp.data
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
},
|
||||
//查询运动类型
|
||||
async exerciseType() {
|
||||
const resp = await this.$axios.post(loc() + "/exerciseType")
|
||||
if (resp.code === 0) {
|
||||
this.exerciseTypeList = resp.data
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
},
|
||||
//根据运动id查询下面有哪些具体的运动项目
|
||||
async changeExercise_typeList() {
|
||||
const resp = await this.$axios.post(loc() + "/changeExercise_typeList", { exerciseType: this.formData.exerciseType })
|
||||
if (resp.code === 0) {
|
||||
this.exerciseTypeList2 = resp.data
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
},
|
||||
//查询距离
|
||||
async focusDistance() {
|
||||
const resp = await this.$axios.post(loc() + "/focusDistance")
|
||||
if (resp.code === 0) {
|
||||
this.distanceList = resp.data
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
},
|
||||
ageInput(val) {
|
||||
const year = new Date().getFullYear()
|
||||
this.$set(this.pageForm, "ageYear", year - val)
|
||||
},
|
||||
async doAssignmentDate() {
|
||||
const aa = this.multipleSelection.map((v) => {
|
||||
if (v.isInterest) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
if (aa.find((v) => v == true)) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "您勾选的列表中包含趣味项目,趣味项目暂不需要年龄限制",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
if (this.multipleSelection.length === 0) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "请先在旁边勾选需要设置的项目",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (this.pageForm.ageDate == null) {
|
||||
this.$notify({
|
||||
title: "警告",
|
||||
message: "请选择需要赋值的时间段",
|
||||
type: "warning"
|
||||
})
|
||||
return
|
||||
}
|
||||
const confirm = await this.$confirm("确定要勾选的项目赋年龄限制吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
const data = await this.$axios.post(loc() + "/doAssignmentDate", {
|
||||
startAgeDate: this.pageForm.ageDate[0],
|
||||
endAgeDate: this.pageForm.ageDate[1],
|
||||
eventIds: JSON.stringify(this.multipleSelection.map((v) => v.id))
|
||||
})
|
||||
this.$notify.success({ title: "成功", message: data.msg })
|
||||
this.$refs.multipleTable.clearSelection()
|
||||
this.doSearch()
|
||||
}
|
||||
},
|
||||
openAdd() {
|
||||
this.$refs.guava.edit()
|
||||
if (this.$refs["form"]) {
|
||||
this.$refs["form"].resetFields()
|
||||
}
|
||||
this.formData = {}
|
||||
},
|
||||
pageData() {
|
||||
this.tabLoading = true
|
||||
const pageForm = clone(this.pageForm)
|
||||
pageForm.isMenWomen = JSON.stringify(pageForm.isMenWomen)
|
||||
this.$axios.post("/platform/activity/basic/event/pageData", pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
async created() {
|
||||
this.pageData()
|
||||
this.ageInput(this.pageForm.age)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,243 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-row :gutter="10" type="flex" style="height: calc(100vh - 126px)">
|
||||
<el-col :span="5">
|
||||
<el-card shadow="never" style="height: 100%" body-style="{ height: '100%' }">
|
||||
<div style="max-height: calc(100vh - 170px); overflow-y: auto">
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
:default-expanded-keys="['7e3f5fcbc24f442dbb3a6cc9412e7960']"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
:props="{
|
||||
children: 'child',
|
||||
label: 'name'
|
||||
}"
|
||||
@node-click="treeNodeClick"
|
||||
highlight-current
|
||||
node-key="id"
|
||||
ref="treeRef"
|
||||
>
|
||||
<template slot-scope="{ node, data }">
|
||||
<span class="el-tree-node__label">
|
||||
<i class="el-icon-folder-opened" v-if="node.level===1"></i>
|
||||
<i class="el-icon-folder" v-else></i>
|
||||
{{node.label}}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-card shadow="never" style="height: 100%">
|
||||
<table-tool>
|
||||
<el-button @click="openBasicType" size="small" type="primary">
|
||||
<i class="el-icon-plus"></i>
|
||||
新建基础类型
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" border size="small">
|
||||
<el-table-column label="序号" type="index" width="100px">
|
||||
<template scope="scope">
|
||||
<span>{{scope.$index+(pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="名称" prop="name"></el-table-column>
|
||||
<el-table-column align="center" label="代码" prop="code"></el-table-column>
|
||||
<el-table-column align="center" label="备注" prop="note" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" label="操作" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-dropdown @command="dropdownCommand">
|
||||
<el-button plain size="mini">
|
||||
<i class="ti-settings"></i>
|
||||
<span class="ti-angle-down"></span>
|
||||
</el-button>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item :command="{type:'edit',data:row}">编辑</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'delete',data:row}">删除</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-size="pageForm.pageSize"
|
||||
:page-sizes="[10, 20, 30, 50]"
|
||||
:total="pageForm.totalCount"
|
||||
@current-change="(val)=>{pageForm.pageNumber = val;pageData()}"
|
||||
@size-change="(val)=>{pageForm.pageSize = val;pageData()}"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
></el-pagination>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-dialog :close-on-click-modal="false" :title="formData.id?'编辑':'新增'" :visible.sync="basicTypeDialog" width="50%">
|
||||
<el-form :model="formData" :rules="rules" label-width="100px" ref="addForm">
|
||||
<el-row :gutter="30">
|
||||
<el-form-item label="上级名称" label-width="110px" prop="name">
|
||||
<el-input :value="checkData.name" disabled maxlength="100" type="text"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="基础类型名称" label-width="110px" prop="name">
|
||||
<el-input maxlength="100" placeholder="请输入基础类型名称" type="text" v-model="formData.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="基础类型代码" label-width="110px" prop="code">
|
||||
<el-input maxlength="100" placeholder="请输入基础类型名称" type="text" v-model="formData.code"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="checkData.id === 'f708d6343c9542b791d32532f860d120'" label="服装项目" label-width="110px" prop="clothing">
|
||||
<el-radio-group v-model="formData.clothing">
|
||||
<el-radio-button :label="true">是</el-radio-button>
|
||||
<el-radio-button :label="false">否</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="基础类型备注" label-width="110px" prop="note">
|
||||
<el-input maxlength="50" placeholder="请输入基础类型备注" type="textarea" v-model="formData.note"></el-input>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 30px">
|
||||
<el-button @click="!formData.id?doAddType():doEditType()" type="primary">提 交</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
filterText: null,
|
||||
treeData: [],
|
||||
|
||||
currentTreeNode: null,
|
||||
currentTreeData: null,
|
||||
|
||||
checkData: {},
|
||||
tableData: [],
|
||||
|
||||
basicTypeDialog: false,
|
||||
|
||||
rules: {
|
||||
name: [{ required: true, message: "必填", trigger: ["change", "blur"] }],
|
||||
code: [{ required: true, message: "必填", trigger: ["change", "blur"] }]
|
||||
},
|
||||
|
||||
pageForm: {
|
||||
parentId: null
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
treeNodeClick(data, node) {
|
||||
this.currentTreeData = data
|
||||
this.currentTreeNode = node
|
||||
this.checkData = data
|
||||
this.pageForm.parentId = data.id
|
||||
this.doSearch()
|
||||
},
|
||||
filterNode() {},
|
||||
|
||||
getTreeData() {
|
||||
this.$axios.post(loc() + "/tree").then((res) => {
|
||||
this.treeData = res.data
|
||||
if (this.treeData) {
|
||||
this.checkData = this.treeData[0]
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
dropdownCommand: function (command) {
|
||||
const { type, data } = command
|
||||
if (type == "edit") {
|
||||
this.openEditType(data)
|
||||
} else if (type == "delete") {
|
||||
this.doDelete(data.id)
|
||||
}
|
||||
},
|
||||
|
||||
async openBasicType() {
|
||||
const { id } = this.checkData
|
||||
this.formData = {
|
||||
parentId: id
|
||||
}
|
||||
this.basicTypeDialog = true
|
||||
if (this.$refs["addForm"]) {
|
||||
this.$refs["addForm"].resetFields()
|
||||
}
|
||||
},
|
||||
doAddType() {
|
||||
this.$refs["addForm"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
const resp = await this.$axios.post(loc() + "/doAdd", this.formData)
|
||||
if (resp.code === 0) {
|
||||
this.basicTypeDialog = false
|
||||
this.doSearch()
|
||||
this.getTreeData()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
doEditType() {
|
||||
this.$refs["addForm"].validate(async (valid) => {
|
||||
if (valid) {
|
||||
const resp = await this.$axios.post(loc() + "/doEditType", this.formData)
|
||||
this.basicTypeDialog = false
|
||||
if (resp.code === 0) {
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
openEditType(row) {
|
||||
this.formData = {
|
||||
id: row.id,
|
||||
name: row.name,
|
||||
code: row.code,
|
||||
note: row.note,
|
||||
clothing: row.clothing
|
||||
}
|
||||
this.basicTypeDialog = true
|
||||
},
|
||||
doDelete(id) {
|
||||
this.$confirm("确定要删除该机构吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning",
|
||||
callback: async (a, b) => {
|
||||
if ("confirm" == a) {
|
||||
const resp = await this.$axios.post(loc() + "/doDelete", { id: id })
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
this.doSearch()
|
||||
this.getTreeData()
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.getTreeData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
+165
@@ -0,0 +1,165 @@
|
||||
const ACTIVITY_BASIC_BRANCH_UNION_MANAGE = {
|
||||
template: `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter)">
|
||||
<div class="search">
|
||||
<div class="search-item">
|
||||
<el-input placeholder="请输入分工会名称" clearable v-model="pageForm.searchKeyword"></el-input>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<table-tool>
|
||||
<el-button @click="openAdd" icon="ti-plus" type="primary" style="margin-left: auto" size="small">新建临时分工会</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" border size="small" height="calc(100vh - 375px)">
|
||||
<el-table-column label="序号" type="index" :index="indexMethod" width="100px"></el-table-column>
|
||||
<el-table-column prop="name" label="工会名称"></el-table-column>
|
||||
<el-table-column prop="unionCode" label="工会编码"></el-table-column>
|
||||
<el-table-column prop="userNames" label="管理员"></el-table-column>
|
||||
<el-table-column label="操作" width="290">
|
||||
<template slot-scope="{row}">
|
||||
<el-button size="mini" @click="openAdmin(row.id)">设置管理员</el-button>
|
||||
<el-button type="primary" size="mini" @click="openEdit(row.id)">编辑</el-button>
|
||||
<el-button type="danger" size="mini" @click="doDeleteUnion(row.id,true)" v-if="row.unitnum===0">删除</el-button>
|
||||
<el-button type="danger" size="mini"
|
||||
v-if="row.unitnum>0&&row.source==='activity'"
|
||||
@click="doDeleteUnion(row.id,false)">还原单位
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
<el-dialog :title="formData.id ? '编辑分工会' : '新增分工会'" :visible.sync="dialogFormVisible" width="600px">
|
||||
<el-form :model="formData" ref="form" size="small" label-width="80px">
|
||||
<el-form-item label="工会名称">
|
||||
<el-input v-model="formData.name" maxlength="100" placeholder="工会名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="工会编码">
|
||||
<el-input v-model="formData.unionCode" maxlength="100" placeholder="工会编码"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="联系电话">
|
||||
<el-input v-model="formData.telephone" maxlength="100" placeholder="联系电话"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="校区" prop="campus">
|
||||
<el-select v-model="formData.campus" maxlength="20" placeholder="校区"></el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog title="设置管理员" :visible.sync="dialogGlyFormVisible" width="50%">
|
||||
<div style="width: 100%;">
|
||||
<el-transfer
|
||||
:titles="['单位成员', '管理员']"
|
||||
filterable
|
||||
:props="{
|
||||
key: 'id',
|
||||
label: 'name'
|
||||
}"
|
||||
:filter-method="adminFilterMethod"
|
||||
v-model="userAdminIds"
|
||||
:data="userData">
|
||||
</el-transfer>
|
||||
</div>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogGlyFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmitAdmin">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
dialogGlyFormVisible: false,
|
||||
formData: {},
|
||||
userData: [],
|
||||
userAdminIds: [],
|
||||
unionId: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
pageData() {
|
||||
this.$axios.post("/platform/activity/basic/union/pageData", this.pageForm).then((res) => {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
})
|
||||
},
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formData = {}
|
||||
})
|
||||
},
|
||||
openEdit(id) {
|
||||
this.dialogFormVisible = true
|
||||
this.$axios.post("/platform/activity/basic/union/findOne", { id }).then((res) => {
|
||||
this.formData = res.data
|
||||
})
|
||||
},
|
||||
doSubmit() {
|
||||
this.$axios.post("/platform/activity/basic/union/" + (this.formData.id ? "update" : "insert"), this.formData).then((res) => {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
this.$emit("refresh", null)
|
||||
})
|
||||
},
|
||||
doDeleteUnion(id, flag) {
|
||||
let msg = flag ? "请确认是否要删除本联合工会,一旦删除相关数据将无法查询!!!" : "选择后系统将组成单位还原到所在分工会!!!"
|
||||
|
||||
this.$confirm(msg, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
}).then(() => {
|
||||
this.$axios.post("/platform/activity/basic/union/doDeleteUnion", { id, flag }).then((res) => {
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
this.$emit("refresh", null)
|
||||
})
|
||||
})
|
||||
},
|
||||
adminFilterMethod(query, item) {
|
||||
return item.name.indexOf(query) > -1
|
||||
},
|
||||
openAdmin(id) {
|
||||
this.unionId = id
|
||||
this.$axios.post("/platform/activity/basic/union/getUnionAdminAndUser", { id: id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.userData = res.data.userData
|
||||
this.userAdminIds = res.data.userAdminIds
|
||||
}
|
||||
})
|
||||
this.dialogGlyFormVisible = true
|
||||
},
|
||||
doSubmitAdmin() {
|
||||
this.$axios
|
||||
.post("/platform/activity/basic/union/doSubmitAdmin", {
|
||||
unionId: this.unionId,
|
||||
userIds: JSON.stringify(this.userAdminIds)
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.doSearch()
|
||||
this.$message.success(res.msg)
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
this.dialogGlyFormVisible = false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
const ACTIVITY_BASIC_BRANCH_UNION_PART_UNIT_MANAGE = {
|
||||
template: `
|
||||
<div>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-row type="flex" style="column-gap: 10px">
|
||||
<el-input
|
||||
placeholder="请输入内容"
|
||||
size="small"
|
||||
style="width: 400px"
|
||||
clearable
|
||||
v-model="pageForm.searchKeyword"
|
||||
@keyup.enter.native="doSearch"
|
||||
></el-input>
|
||||
<el-button type="primary" size="small" icon="el-icon-search" @click="doSearch"></el-button>
|
||||
<el-button type="primary" size="small" icon="el-icon-setting" style="margin-left: auto" @click="openAdd">设置组成单位</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter); margin-top: 10px">
|
||||
<el-table :data="tableData" border size="small">
|
||||
<el-table-column type="index" label="序号"></el-table-column>
|
||||
<el-table-column prop="unitcode" label="单位编码"></el-table-column>
|
||||
<el-table-column prop="name" label="单位名称"></el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<el-dialog title="设置组成单位" :visible.sync="dialogFormVisible" width="1000px" :close-on-click-modal="false">
|
||||
<el-transfer
|
||||
ref="transferRef"
|
||||
:titles="['可选二级单位', '当前成员单位']"
|
||||
filterable
|
||||
:props="{
|
||||
key: 'id',
|
||||
label: 'name'
|
||||
}"
|
||||
:filter-method="filterUnit"
|
||||
v-model="selectUnitsIds"
|
||||
:data="allUnits"
|
||||
></el-transfer>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogFormVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doSubmit">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
`,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
dialogFormVisible: false,
|
||||
selectUnitsIds: [],
|
||||
allUnits: [],
|
||||
unionId: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openAdd() {
|
||||
this.dialogFormVisible = true
|
||||
if (this.$refs.transferRef) {
|
||||
this.$refs.transferRef.clearQuery("left")
|
||||
this.$refs.transferRef.clearQuery("right")
|
||||
}
|
||||
this.$axios.post("/platform/activity/basic/union/branchUnionPartUnitTransferData", { unionId: this.unionId }).then((res) => {
|
||||
this.selectUnitsIds = res.data.selectUnitIds
|
||||
this.allUnits = res.data.allUnits
|
||||
})
|
||||
},
|
||||
|
||||
filterUnit(query, item) {
|
||||
return item.name.indexOf(query) > -1
|
||||
},
|
||||
|
||||
doSubmit() {
|
||||
this.$axios
|
||||
.post("/platform/activity/basic/union/branchUnionPartUnitSet", {
|
||||
unitIds: JSON.stringify(this.selectUnitsIds),
|
||||
unionId: this.unionId
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.dialogFormVisible = false
|
||||
this.$message.success(res.msg)
|
||||
this.doSearch()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
pageData(unionId) {
|
||||
if (unionId) {
|
||||
this.unionId = unionId
|
||||
}
|
||||
this.$axios
|
||||
.post("/platform/activity/basic/union/branchUnionPartUnitPageData", {
|
||||
...this.pageForm,
|
||||
unionId: this.unionId
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-row :gutter="10" type="flex" style="height: calc(100vh - 126px)">
|
||||
<el-col :span="5">
|
||||
<el-card shadow="never" style="height: 100%" body-style="{ height: '100%' }">
|
||||
<div style="max-height: calc(100vh - 160px); overflow-y: auto">
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
:expand-on-click-node="false"
|
||||
:props="{
|
||||
children: 'children',
|
||||
label: 'name'
|
||||
}"
|
||||
@node-click="treeNodeClick"
|
||||
default-expand-all
|
||||
highlight-current
|
||||
ref="treeRef"
|
||||
>
|
||||
<template slot-scope="{ node, data }">
|
||||
<span class="el-tree-node__label">
|
||||
<i class="el-icon-folder-opened" v-if="node.level===1"></i>
|
||||
<i class="el-icon-folder" v-else></i>
|
||||
{{node.label}}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="19">
|
||||
<el-card style="height: 100%" shadow="never">
|
||||
<template v-if="currentTreeNode==null || currentTreeNode.level===1">
|
||||
<activity-basic-branch-union-manage @refresh="getTreeData()"></activity-basic-branch-union-manage>
|
||||
</template>
|
||||
<template v-if="currentTreeNode && currentTreeNode.level===2">
|
||||
<activity-basic-branch-union-part-unit-manage ref="unitManage"></activity-basic-branch-union-part-unit-manage>
|
||||
</template>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("activityBasicBranchUnionManage.js"){}#-->
|
||||
<!--#include("activityBasicBranchUnionPartUnitManage.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
treeData: [],
|
||||
currentTreeNode: null,
|
||||
currentTreeData: null
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-basic-branch-union-manage": ACTIVITY_BASIC_BRANCH_UNION_MANAGE,
|
||||
"activity-basic-branch-union-part-unit-manage": ACTIVITY_BASIC_BRANCH_UNION_PART_UNIT_MANAGE
|
||||
},
|
||||
methods: {
|
||||
treeNodeClick(data, node) {
|
||||
this.currentTreeData = data
|
||||
this.currentTreeNode = node
|
||||
if (node.level === 2) {
|
||||
setTimeout(() => {
|
||||
this.$refs.unitManage.pageData(data.id)
|
||||
}, 300)
|
||||
}
|
||||
},
|
||||
getTreeData() {
|
||||
this.$axios.post(loc() + "/tree").then((res) => {
|
||||
this.treeData = res.data
|
||||
if (this.treeData) {
|
||||
this.checkData = this.treeData[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.getTreeData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,149 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<el-row :gutter="10" type="flex" style="height: calc(100vh - 126px)">
|
||||
<el-col :span="5">
|
||||
<el-card shadow="never" style="height: 100%" body-style="{ height: '100%' }">
|
||||
<div style="max-height: calc(100vh - 160px); overflow-y: auto">
|
||||
<el-tree
|
||||
:data="treeData"
|
||||
:default-expanded-keys="['1']"
|
||||
:expand-on-click-node="false"
|
||||
:props="{
|
||||
children: 'child',
|
||||
label: 'name'
|
||||
}"
|
||||
@node-click="treeNodeClick"
|
||||
highlight-current
|
||||
node-key="id"
|
||||
ref="treeRef"
|
||||
>
|
||||
<template slot-scope="{ node, data }">
|
||||
<span class="el-tree-node__label">
|
||||
<i class="el-icon-folder-opened" v-if="node.level===1"></i>
|
||||
<i class="el-icon-folder" v-else></i>
|
||||
{{node.label}}
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="19">
|
||||
<el-card shadow="never" style="height: 100%">
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter)">
|
||||
<div class="search">
|
||||
<div class="search-item" style="width: 25%">
|
||||
<el-input
|
||||
@keyup.enter.native="doSearch"
|
||||
clearable
|
||||
placeholder="请输入内容"
|
||||
style="width: 100%"
|
||||
v-model="pageForm.searchKeyword"
|
||||
>
|
||||
<el-select placeholder="查询类型" slot="prepend" style="width: 110px" v-model="pageForm.searchName">
|
||||
<el-option label="单位名称" value="name"></el-option>
|
||||
<el-option label="单位代码" value="unitcode"></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="search-item">
|
||||
<el-button @click="doSearch" icon="el-icon-search" type="primary">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
<el-card shadow="never" style="border: 1px solid var(--border-color-lighter)">
|
||||
<table-tool></table-tool>
|
||||
<el-table :data="tableData" height="calc(100vh - 380px)" border size="small">
|
||||
<el-table-column label="序号" type="index">
|
||||
<template scope="scope">
|
||||
<span>{{scope.$index+(pageForm.pageNumber - 1) * pageForm.pageSize + 1}}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="单位名称" prop="name"></el-table-column>
|
||||
<el-table-column align="center" label="单位代码" prop="unitcode"></el-table-column>
|
||||
<el-table-column align="center" label="单位等级" prop="unitLevel">
|
||||
<template slot-scope="{row}">
|
||||
<el-button round size="mini" style="vertical-align: middle" type="success" v-if="row.unitLevel==1">1级</el-button>
|
||||
<el-button round size="mini" style="vertical-align: middle" type="warning" v-if="row.unitLevel==2">2级</el-button>
|
||||
<el-button round size="mini" style="vertical-align: middle" type="danger" v-if="row.unitLevel==3">3级</el-button>
|
||||
<el-button round size="mini" style="vertical-align: middle" type="info" v-if="row.unitLevel>3">
|
||||
{{row.unitlevel}}级
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" width="100px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="doDelete(row.id)" size="mini" type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
pageForm: {
|
||||
searchName: "name"
|
||||
},
|
||||
treeData: [],
|
||||
currentTreeNode: null,
|
||||
currentTreeData: null
|
||||
}
|
||||
},
|
||||
components: {},
|
||||
methods: {
|
||||
async doDelete(id) {
|
||||
const confirm = await this.$confirm("确定要删除该单位吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
const resp = await this.$axios.post(loc() + "/doDelete", { id })
|
||||
if (resp.code === 0) {
|
||||
this.getTreeData()
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
}
|
||||
},
|
||||
treeNodeClick(data, node) {
|
||||
this.currentTreeData = data
|
||||
this.currentTreeNode = node
|
||||
this.pageForm.parentId = data.id
|
||||
this.doSearch()
|
||||
},
|
||||
getTreeData() {
|
||||
this.$axios.post(loc() + "/tree").then((res) => {
|
||||
this.treeData = res.data
|
||||
if (this.treeData) {
|
||||
this.currentTreeData = this.treeData[0]
|
||||
}
|
||||
})
|
||||
},
|
||||
openUnit() {}
|
||||
},
|
||||
created() {
|
||||
this.pageData()
|
||||
this.getTreeData()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,26 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<user-scope ref="userScope"></user-scope>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
components: {
|
||||
"user-scope": httpVueLoader("/components/module/activity/UserScope.vue?v=" + new Date().getDate())
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,26 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava>
|
||||
<user-scope-data ref="userDataScope"></user-scope-data>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
new Vue({
|
||||
el: "#app",
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
components: {
|
||||
"user-scope-data": httpVueLoader("/components/module/activity/UserDataScope.vue?v=" + new Date().getDate())
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,33 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<activity-culture-apply-activity :activity_type="activityType" ref="activityApply"></activity-culture-apply-activity>
|
||||
</guava>
|
||||
</div>
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("../../common/applyActivity.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
activityType: 40003
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-culture-apply-activity": ACTIVITY_CULTURE_APPLY_ACTIVITY
|
||||
},
|
||||
methods: {},
|
||||
async created() {
|
||||
setTimeout(()=>{this.$refs.activityApply.init()},500)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,32 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<activity-culture-apply-user :activity_type="activityType"></activity-culture-apply-user>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("../../common/applyUser.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
activityType: 40003
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-culture-apply-user": ACTIVITY_CULTURE_APPLY_USER
|
||||
},
|
||||
methods: {},
|
||||
async created() {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,30 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<activity-culture-audit-activity :activity_type="activityType"></activity-culture-audit-activity>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("../../common/auditActivity.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
activityType: 40003
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-culture-audit-activity": ACTIVITY_CULTURE_AUDIT_ACTIVITY
|
||||
},
|
||||
methods: {},
|
||||
async created() {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
@@ -0,0 +1,30 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<activity-culture-info-manage :activity_type="activityType"></activity-culture-info-manage>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("../../common/infoMange.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
activityType: 40003
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-culture-info-manage": ACTIVITY_CULTURE_INFO_MANAGE
|
||||
},
|
||||
methods: {},
|
||||
async created() {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
<!--#
|
||||
layout("/layouts/platform.html"){
|
||||
#-->
|
||||
|
||||
<div id="app" v-cloak>
|
||||
<guava ref="guava">
|
||||
<activity-culture-user-statistics :activity_type="activityType"></activity-culture-user-statistics>
|
||||
</guava>
|
||||
</div>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
<!--#include("../../common/userStatistics.js"){}#-->
|
||||
new Vue({
|
||||
el: "#app",
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
activityType: 40003
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-culture-user-statistics": ACTIVITY_CULTURE_USER_STATISTICS
|
||||
},
|
||||
methods: {},
|
||||
async created() {}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!--#
|
||||
}
|
||||
#-->
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,154 @@
|
||||
<!--#include("infoActivity.js"){}#-->
|
||||
<!--#include("singleSignUp.js"){}#-->
|
||||
const ACTIVITY_CULTURE_APPLY_USER = {
|
||||
template: /*language=HTML*/ `
|
||||
<guava ref="guava">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<search @search="doSearch">
|
||||
<search-item label="活动时间">
|
||||
<el-date-picker
|
||||
:clearable="false"
|
||||
placeholder="活动时间"
|
||||
style="width: 100%"
|
||||
type="year"
|
||||
v-model="pageForm.year"
|
||||
value-format="yyyy"
|
||||
></el-date-picker>
|
||||
</search-item>
|
||||
|
||||
<search-item label="活动名称">
|
||||
<el-input clearable placeholder="请输入活动名称"
|
||||
v-model="pageForm.name"></el-input>
|
||||
</search-item>
|
||||
<search-item label="">
|
||||
<el-radio-group v-model="pageForm.state" @change="doSearch">
|
||||
<el-radio-button label="1">全部</el-radio-button>
|
||||
<el-radio-button label="2">报名中</el-radio-button>
|
||||
<el-radio-button label="3">报名已结束</el-radio-button>
|
||||
</el-radio-group>
|
||||
</search-item>
|
||||
</search>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool label="活动列表">
|
||||
<el-radio-group v-model="pageForm.isEnrolled" @change="doSearch" size="mini" class="ml10">
|
||||
<el-radio-button :label="true">已报名</el-radio-button>
|
||||
<el-radio-button :label="false">未报名</el-radio-button>
|
||||
</el-radio-group>
|
||||
</table-tool>
|
||||
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" row-key="id"
|
||||
style="width: 100%;margin-bottom: 20px">
|
||||
<el-table-column :index="indexMethod" label="序号" type="index" width="80px"></el-table-column>
|
||||
<el-table-column
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
:width="column.width"
|
||||
align="center"
|
||||
header-align="center"
|
||||
show-overflow-tooltip
|
||||
v-for="column in tableColumns">
|
||||
<template scope="{row}" v-if="column.prop=='time'">
|
||||
{{row.startTime}} 至 {{row.endTime}}
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop=='projectTypeName'">
|
||||
{{row.projectTypeName}}({{row.projectTypeCode}})
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop=='tussueName'">
|
||||
<span v-if="row.activity_type==40002">{{row.unionname?row.unionname:'暂无'}}</span>
|
||||
<span v-if="row.activity_type==40003">{{row.clubName?row.clubName:'暂无'}}</span>
|
||||
<span v-if="row.activity_type==40001">校工会</span>
|
||||
</template>
|
||||
<template scope="{row}" v-else-if="column.prop=='countUser'">
|
||||
<el-tag size="small">{{row.countUser}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作"
|
||||
prop="userOnline" width="300px">
|
||||
<template slot-scope="{row}">
|
||||
<el-button @click="openView(row)" size="mini" type="primary">
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!row.isEnrolled && $moment(row.applyStartTime).valueOf()<$moment().valueOf() && $moment(row.applyEndTime).valueOf()>$moment().valueOf()"
|
||||
@click="openSign(row)" size="mini" type="primary">
|
||||
报名
|
||||
</el-button>
|
||||
<el-button v-if="row.isEnrolled" @click="openSign(row)" size="mini" type="primary">
|
||||
报名情况
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!--#include("/layouts/pagination.html"){}#-->
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<template #view>
|
||||
<activity-culture-info-activity ref="infoActivity"></activity-culture-info-activity>
|
||||
</template>
|
||||
|
||||
<single-signup ref="singleSignupRef" @refresh="doSearch"></single-signup>
|
||||
</guava>
|
||||
`,
|
||||
store,
|
||||
mixins: [initTableMixins],
|
||||
props: {
|
||||
activity_type: {
|
||||
value: { type: Object, default: "" }
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
submitLoading: false,
|
||||
pageForm: {
|
||||
isEnrolled: false,
|
||||
state: 1,
|
||||
year: new Date().getFullYear() + ""
|
||||
},
|
||||
tableColumns: [
|
||||
{ prop: "name", label: "活动名称" },
|
||||
{ prop: "address", label: "活动地点" },
|
||||
{ prop: "projectTypeName", label: "活动项目类型" },
|
||||
{ prop: "tussueName", label: "举办单位" },
|
||||
{ prop: "time", label: "活动日期" },
|
||||
{ prop: "applyStartTime", label: "报名开始时间" },
|
||||
{ prop: "applyEndTime", label: "报名结束时间" }
|
||||
]
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"activity-culture-info-activity": ACTIVITY_CULTURE_INFO_ACTIVITY,
|
||||
"single-signup": singleSignUp
|
||||
},
|
||||
methods: {
|
||||
//打开报名
|
||||
async openSign(row) {
|
||||
this.$refs.singleSignupRef.onOpen(row.id)
|
||||
},
|
||||
openView(row) {
|
||||
this.$refs.guava.view(() => {
|
||||
this.$refs.infoActivity.onOpen(row)
|
||||
})
|
||||
},
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
this.pageForm.activity_type = this.activity_type
|
||||
this.$axios.post("/platform/activity/culture/applyUser/pageData", this.pageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = res.data.list
|
||||
this.pageForm.totalCount = res.data.totalCount
|
||||
}
|
||||
}).finally(() => {
|
||||
this.tableLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user