first commit
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-breadcrumb separator="/">
|
||||
<el-breadcrumb-item><a @click.prevent="">首页</a></el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="item in breadcrumbMenus" :key="item.id">
|
||||
{{ item.name }}
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "HeaderBreadCrumb",
|
||||
store,
|
||||
computed: {
|
||||
pcModuleMenus() {
|
||||
return this.$store.getters.pcModules
|
||||
},
|
||||
fullMenus() {
|
||||
return this.$store.getters.fullMenus
|
||||
},
|
||||
openedMenus() {
|
||||
return this.$store.state.openedMenus
|
||||
},
|
||||
|
||||
breadcrumbMenus() {
|
||||
if (this.openedMenus && this.openedMenus.length > 0) {
|
||||
const breadcrumbMenus = []
|
||||
this.openedMenus.forEach((menuId) => {
|
||||
const menu = this.fullMenus.find((m) => m.id === menuId)
|
||||
if (menu) {
|
||||
breadcrumbMenus.push({
|
||||
name: menu.name,
|
||||
href: menu.href,
|
||||
id: menu.id,
|
||||
type: menu.type
|
||||
})
|
||||
}
|
||||
})
|
||||
return breadcrumbMenus
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,107 @@
|
||||
<template>
|
||||
<div class="appCenterPopover">
|
||||
<el-popover width="450" placement="top-start" trigger="hover">
|
||||
<el-card shadow="never" class="appCenterCard">
|
||||
<template #header>
|
||||
<el-row type="flex" justify="space-between">
|
||||
<div>
|
||||
<el-link :underline="false">平台</el-link>
|
||||
</div>
|
||||
</el-row>
|
||||
</template>
|
||||
<el-row :gutter="20" v-if="pcModuleMenus && pcModuleMenus.length > 0">
|
||||
<el-col v-for="item in pcModuleMenus" :key="item.id" :span="8" style="margin-bottom: 5px">
|
||||
<div
|
||||
class="entranceDetail"
|
||||
:class="{ 'entranceDetail-selected': item.id === activeModuleId }"
|
||||
@click="enterEntranceModules(item)"
|
||||
>
|
||||
<div class="entranceIcon">
|
||||
<el-image :src="item.icon" style="height: 50px; width: 50px" fit="cover">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
</div>
|
||||
<a href="javascript:">{{ item.name }}</a>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<span href="javascript:" slot="reference" style="font-size: 16px; font-weight: bold">
|
||||
<i class="el-icon-menu"></i>
|
||||
{{ activeModuleName }}
|
||||
<i class="el-icon-sort"></i>
|
||||
<span>切换平台</span>
|
||||
</span>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "HeaderMenuModule",
|
||||
store,
|
||||
computed: {
|
||||
activeModuleId() {
|
||||
return this.$store.state.activeMenuModuleId
|
||||
},
|
||||
activeModuleName() {
|
||||
if (this.pcModuleMenus) {
|
||||
return this.pcModuleMenus.find((m) => m.id === this.activeModuleId)?.name
|
||||
}
|
||||
return null
|
||||
},
|
||||
pcModuleMenus() {
|
||||
return this.$store.getters.pcModules
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
enterEntranceModules(item) {
|
||||
this.$store.commit("setActiveMenuModuleId", item.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.appCenterPopover {
|
||||
padding: 0 !important;
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.el-popover__reference{
|
||||
font-weight: normal!important;
|
||||
}
|
||||
|
||||
.appCenterCard .el-card__header {
|
||||
padding: 8px 10px !important;
|
||||
}
|
||||
|
||||
:deep(.entranceDetail) {
|
||||
border-radius: 6px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
line-height: 1;
|
||||
color: #606060;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.entranceDetail:hover,
|
||||
.entranceDetail-selected {
|
||||
background-color: #e5edff;
|
||||
color: #3177fd;
|
||||
}
|
||||
|
||||
.appCenterCard .entranceIcon {
|
||||
position: relative;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 8px auto 6px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.appCenterCard .entranceDetail a {
|
||||
color: #000000;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,114 @@
|
||||
<template>
|
||||
<div class="ele-notice-group">
|
||||
<el-popover v-model="msgPopover" placement="bottom" width="300" trigger="click" popper-class="ele-notice-pop" style="width: 300px">
|
||||
<div class="ele-notice-list ele-scrollbar-mini">
|
||||
<div class="ele-notice-item">
|
||||
<div
|
||||
v-if="messages.length > 0"
|
||||
class="ele-cell ele-notice-item-wrapper"
|
||||
v-for="msg in messages"
|
||||
:key="msg.id"
|
||||
@click="readMsg(msg, 'system')"
|
||||
>
|
||||
<i class="el-icon-s-comment ele-notice-item-icon"></i>
|
||||
<div class="ele-cell-content">
|
||||
<div class="ele-elip">{{ msg.title }}</div>
|
||||
<div class="ele-text-secondary ele-elip">
|
||||
{{ $moment(msg.sendAt).format("YYYY-MM-DD HH:mm") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-if="messages.length === 0" description="暂无数据"></el-empty>
|
||||
<div class="el-divider el-divider--horizontal"><!----></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ele-cell ele-notice-actions">
|
||||
<a @click.prevent="viewMore" class="ele-cell-content">查看更多</a>
|
||||
</div>
|
||||
<el-badge :value="messages.length" slot="reference" :style="{ animation: messages.length > 0 ? 'bell-shake 0.5s infinite' : '' }">
|
||||
<i class="el-icon-bell" style="color: var(--color-white)"></i>
|
||||
</el-badge>
|
||||
</el-popover>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "HeaderMessage",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
tabActive: "system",
|
||||
unReadNum: {},
|
||||
msgPopover: false,
|
||||
messages: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
totalMsgNum() {
|
||||
// return Object.keys(this.unReadNum).reduce((accumulator, key) => {
|
||||
// return accumulator + this.unReadNum[key]
|
||||
// }, 0)
|
||||
return 0
|
||||
}
|
||||
// messages() {
|
||||
// return this.$store.state.messages
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
removeSystemMsg() {
|
||||
this.$message.warning("暂不支持清空消息")
|
||||
},
|
||||
//获取未读消息
|
||||
getUnReadNum() {
|
||||
this.$axios.post("/platform/sys/msg/user/unread_num").then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
this.unReadNum = resp.data
|
||||
}
|
||||
})
|
||||
},
|
||||
//读取消息
|
||||
readMsg(msg, type) {
|
||||
this.msgPopover = false
|
||||
this.$store.dispatch("pjaxRoute", "/platform/sys/msg/user/all")
|
||||
},
|
||||
viewMore() {
|
||||
this.msgPopover = false
|
||||
this.$store.dispatch("pjaxRoute", "/platform/sys/msg/user/all")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
webSocketPubSub.subscribe("innerMsg", (data) => {
|
||||
console.info("ws:收到系统提醒消息:", data)
|
||||
this.messages = data.list
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.ele-notice-group .el-empty {
|
||||
padding: 0 !important;
|
||||
}
|
||||
.ele-notice-group .el-empty__image {
|
||||
width: 116px !important;
|
||||
}
|
||||
|
||||
@keyframes bell-shake {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
25% {
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
75% {
|
||||
transform: rotate(-5deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<div class="ele-admin-tabs" id="history-menu-tabs">
|
||||
<el-tabs v-model="activeName" tab-position="top" @tab-click="tabClick">
|
||||
<el-tab-pane name="/platform/home">
|
||||
<span slot="label">
|
||||
<i class="el-icon-house" @click.stop="close('/platform/home')"></i>
|
||||
首页
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane v-for="item in menuTabs" :label="item.name" :name="item.id" :key="item.id">
|
||||
<span slot="label">
|
||||
{{ item.name }}
|
||||
<i class="el-icon-close" @click.stop="close(item)"></i>
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dropdown class="ele-admin-tabs-drop" size="small" @command="handleCommand">
|
||||
<span class="el-dropdown-link">
|
||||
<i class="el-icon-arrow-down el-dropdown-selfdefine"></i>
|
||||
</span>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="closeOther" icon="el-icon-remove-outline">关闭其他</el-dropdown-item>
|
||||
<el-dropdown-item command="closeAll" icon="el-icon-circle-close">关闭全部</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "HeaderTabPage",
|
||||
store,
|
||||
data() {
|
||||
return {
|
||||
activeName: "/platform/home"
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeMenuIndex() {
|
||||
return this.$store.state.activeMenuIndex
|
||||
},
|
||||
fullMenus() {
|
||||
return this.$store.getters.fullMenus
|
||||
},
|
||||
historyMenuIds() {
|
||||
return this.$store.getters.historyMenuIds
|
||||
},
|
||||
menuTabs() {
|
||||
if (this.historyMenuIds && this.historyMenuIds.length > 0) {
|
||||
return this.fullMenus.filter((menu) => this.historyMenuIds.includes(menu.id))
|
||||
}
|
||||
return []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
tabClick({ name }) {
|
||||
//首页的因为没有菜单直接跳
|
||||
if (name === "/platform/home") {
|
||||
this.$store.dispatch("pjaxRoute", "/platform/home")
|
||||
return
|
||||
}
|
||||
const menu = this.fullMenus.find((menu) => menu.id === name)
|
||||
this.$store.dispatch("pjaxRoute", menu.href)
|
||||
},
|
||||
close(item) {
|
||||
this.$store.commit("removeHistoryMenuId", item.id)
|
||||
if (this.historyMenuIds.length === 0) {
|
||||
this.$store.dispatch("pjaxRoute", "/platform/home")
|
||||
return
|
||||
}
|
||||
const historyMenuId = this.historyMenuIds[this.historyMenuIds.length - 1]
|
||||
const menu = this.fullMenus.find((menu) => menu.id === historyMenuId)
|
||||
this.$store.dispatch("pjaxRoute", menu.href)
|
||||
},
|
||||
handleCommand(command) {
|
||||
if (command === "closeOther") {
|
||||
const otherMenuIds = this.historyMenuIds.filter((id) => id !== this.activeMenuIndex)
|
||||
this.$store.commit("removeHistoryMenuIds", otherMenuIds)
|
||||
} else if (command === "closeAll") {
|
||||
this.$store.commit("clearHistoryMenuIds")
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
activeMenuIndex: {
|
||||
handler: function (val) {
|
||||
if (val) {
|
||||
this.activeName = this.activeMenuIndex
|
||||
} else {
|
||||
this.activeName = "/platform/home"
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$store.commit("addHistoryMenuId", this.activeMenuIndex)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ele-admin-tabs {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
background: var(--color-white);
|
||||
-webkit-box-shadow: var(--header-light-shadow);
|
||||
box-shadow: var(--header-light-shadow);
|
||||
padding: 0 40px 0 15px;
|
||||
z-index: 99;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ele-admin-tabs > .el-tabs {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
color: var(--color-text-secondary);
|
||||
-webkit-transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
padding: 0 15px !important;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item.is-closable {
|
||||
padding-right: 8px !important;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item:after {
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: var(--color-primary);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item:hover {
|
||||
color: var(--color-text-primary);
|
||||
background: var(--header-tool-hover-bg);
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item.is-active {
|
||||
color: var(--color-primary);
|
||||
background: var(--color-primary-1);
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item.is-active:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item .el-icon-close {
|
||||
font-size: 13px;
|
||||
margin-left: 4px;
|
||||
color: var(--color-text-secondary);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item .el-icon-close:before {
|
||||
-webkit-transform: none;
|
||||
transform: none;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item .el-icon-close:hover {
|
||||
color: #fff;
|
||||
background: var(--color-danger);
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item:focus.is-active.is-focus:not(:active) {
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__item.sortable-ghost {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__active-bar,
|
||||
.ele-admin-tabs .el-tabs__content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__nav-wrap {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__nav-wrap:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__nav-wrap.is-scrollable {
|
||||
padding: 0 39px 0 40px;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__nav-next,
|
||||
.ele-admin-tabs .el-tabs__nav-prev {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 16px;
|
||||
-webkit-transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .el-tabs__nav-next:hover,
|
||||
.ele-admin-tabs .el-tabs__nav-prev:hover {
|
||||
color: var(--color-text-primary);
|
||||
background: var(--header-tool-hover-bg);
|
||||
}
|
||||
|
||||
.ele-admin-tabs .ele-admin-tabs-drop {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .ele-admin-tabs-drop .el-icon-arrow-down {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
color: var(--color-text-secondary);
|
||||
font-size: 16px;
|
||||
-webkit-transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
transition:
|
||||
background-color 0.2s,
|
||||
color 0.2s;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .ele-admin-tabs-drop .el-icon-arrow-down:hover {
|
||||
color: var(--color-text-primary);
|
||||
background: var(--header-tool-hover-bg);
|
||||
}
|
||||
|
||||
.ele-admin-tabs .ele-admin-tab-context-trigger {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ele-admin-tabs .ele-admin-tab-context-menu {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 6px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,105 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dropdown @command="handleCommand">
|
||||
<div class="ele-admin-header-avatar el-dropdown-selfdefine">
|
||||
<span class="el-avatar el-avatar--circle">
|
||||
<img
|
||||
v-if="!$store.state.user.sex || $store.state.user.sex === '男'"
|
||||
src="/assets/platform/img/home/avatar_boy.png"
|
||||
style="object-fit: cover"
|
||||
/>
|
||||
<img v-else alt="" src="/assets/platform/img/home/avatar_girl.png" />
|
||||
</span>
|
||||
<span class="hidden-xs-only" style="color: var(--color-white)">
|
||||
{{ $store.state.user.username }}
|
||||
</span>
|
||||
<i class="el-icon-arrow-down" style="color: var(--color-white)"></i>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown">
|
||||
<el-dropdown-item command="userInfo">个人信息</el-dropdown-item>
|
||||
<el-dropdown-item command="signature">我的签名</el-dropdown-item>
|
||||
<el-dropdown-item command="logout">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
|
||||
<el-dialog :visible.sync="memberInfoDialogVisible" title="我的信息" width="70%" append-to-body>
|
||||
<user-info ref="userInfoRef"></user-info>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="signatureDialogVisible" title="我的电子签名" width="35%" append-to-body>
|
||||
<div class="flx">
|
||||
<div v-if="!showSignature" class="p10" style="width: calc(100% - 125px)">
|
||||
<el-image style="width: 100%; height: 189px; border: 1px dashed #000" :src="userSignatureUrl">
|
||||
<template slot="error">
|
||||
<div style="height: 100%; display: flex; justify-content: center; align-items: center">暂无签名信息</div>
|
||||
</template>
|
||||
</el-image>
|
||||
</div>
|
||||
<div class="flx-center-center" style="flex-direction: column">
|
||||
<qrcode :options="{ width: 126 }" :value="signatureAddress" class="signature-qrcode"></qrcode>
|
||||
<el-button icon="el-icon-refresh" size="small" type="primary" @click="getUserSignature(true)">刷新</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-alert title="扫描二维码可重新签名,手机上签名成功后可点击刷新查看。" type="success"></el-alert>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "HeaderUserAvatar",
|
||||
store,
|
||||
components: {
|
||||
"user-info": httpVueLoader("/components/module/userInfo/index.vue")
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
memberInfoDialogVisible: false,
|
||||
showSignature: false,
|
||||
userSignatureUrl: null,
|
||||
signatureDialogVisible: false,
|
||||
signatureAddress: window.location.origin + "/platform/signature/scanPcCode?origin=user",
|
||||
userInfoDialogVisible: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleCommand(command) {
|
||||
if (command === "logout") {
|
||||
window.location.href = base + "/platform/login/logout"
|
||||
} else if (command === "userInfo") {
|
||||
this.memberInfoDialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.$refs.userInfoRef.onOpen(this.$store.state.user.id)
|
||||
})
|
||||
} else if (command === "signature") {
|
||||
this.signatureDialogVisible = true
|
||||
this.getUserSignature()
|
||||
console.log(this.signatureAddress)
|
||||
}
|
||||
},
|
||||
getUserSignature(isRefresh = false) {
|
||||
this.$axios.post("/platform/signature/get").then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (isRefresh) {
|
||||
this.$message.success("刷新成功")
|
||||
} else {
|
||||
if (!res.data || !res.data.signature) {
|
||||
this.$message.warning("未获取到签名信息")
|
||||
}
|
||||
}
|
||||
this.userSignatureUrl = res.data && res.data.signature
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
webSocketPubSub.subscribe("pc-scan-code-manage-signature", (data) => {
|
||||
console.info("ws:收到pc端我的电子签名扫描二维码返回结果:", data)
|
||||
this.userSignatureUrl = data.url
|
||||
this.$message.success("获取成功")
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,111 @@
|
||||
<template>
|
||||
<div class="ele-admin-sidebar-menus">
|
||||
<el-scrollbar>
|
||||
<el-menu unique-opened :default-active="activeMenuIndex" :default-openeds="openedMenus" @select="menuSelect" class="ele-menu-dark">
|
||||
<menu-list :menus="leftMenus" :depth="0" :key="$store.state.activeMenuModuleId"></menu-list>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "SideBarLeftMenu",
|
||||
store,
|
||||
components: {
|
||||
"menu-list": {
|
||||
name: "menu-list",
|
||||
template:
|
||||
/*language=HTML*/
|
||||
`
|
||||
<div>
|
||||
<template v-for="(item, index) in menus">
|
||||
<el-submenu :index="item.id" v-if="item.children && item.children.length > 0">
|
||||
<template #title>
|
||||
<svg-icon :name="item.icon" v-if="item.icon" :size="18"></svg-icon>
|
||||
<i v-else class="el-icon-paperclip"></i>
|
||||
<span>{{ item.name }}</span>
|
||||
</template>
|
||||
<menu-list :menus="item.children" :depth="depth + 1" />
|
||||
</el-submenu>
|
||||
<el-menu-item :index="item.id" v-else>
|
||||
<a :href="item.href" data-pjax>
|
||||
<svg-icon :name="item.icon" v-if="item.icon" :size="18"></svg-icon>
|
||||
<i v-else :class="item.icon ? item.icon : 'el-icon-menu'"></i>
|
||||
<span>{{ item.name }}</span>
|
||||
</a>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</div>
|
||||
`,
|
||||
props: ["menus", "depth"]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
//模块
|
||||
pcModuleMenus() {
|
||||
return this.$store.getters.pcModules
|
||||
},
|
||||
//左侧菜单 根据模块id动态变化
|
||||
leftMenus() {
|
||||
const activeMenuModuleId = this.$store.state.activeMenuModuleId
|
||||
if (activeMenuModuleId) {
|
||||
if (this.pcModuleMenus) {
|
||||
return this.pcModuleMenus.find((module) => module.id === activeMenuModuleId)?.menus
|
||||
}
|
||||
return []
|
||||
}
|
||||
return []
|
||||
},
|
||||
//当前选中菜单
|
||||
activeMenuIndex() {
|
||||
return this.$store.state.activeMenuIndex
|
||||
},
|
||||
//当前打开的菜单 数组 包含所有父菜单
|
||||
openedMenus() {
|
||||
return this.$store.state.openedMenus
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//点击菜单
|
||||
menuSelect(index, indexPath) {
|
||||
this.$store.commit("setActiveMenuIndex", index)
|
||||
this.$store.commit("setOpenedMenus", indexPath)
|
||||
this.$store.commit("addHistoryMenuId", index)
|
||||
},
|
||||
onResize() {
|
||||
if (window.innerWidth < 768) {
|
||||
$("#ele-admin-header.ele-admin-header .ele-admin-header-tool .ele-admin-header-tool-item.collapse-menu")[0].style.display = "flex"
|
||||
document.querySelector("body >div > div.ele-admin-layout").classList.add("ele-admin-collapse")
|
||||
} else {
|
||||
$("#ele-admin-header.ele-admin-header .ele-admin-header-tool .ele-admin-header-tool-item.collapse-menu")[0].style.display = "none"
|
||||
document.querySelector("body >div > div.ele-admin-layout").classList.remove("ele-admin-collapse")
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.$store.dispatch("pjaxRoute", window.location)
|
||||
},
|
||||
mounted() {
|
||||
// 监听窗口大小 并且使用节流
|
||||
this.onResize()
|
||||
this.handleResize = MinDash.throttle(this.onResize, 500)
|
||||
$(window).resize(() => {
|
||||
this.handleResize()
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
$(window).off("resize", this.handleResize)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu-link {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,235 @@
|
||||
<template>
|
||||
<div style="padding: 20px 50px">
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="下载模板" placement="top">
|
||||
<el-card>
|
||||
<el-button size="medium" style="width: 200px"
|
||||
@click="$downLoad('/platform/activity/basic/scope/downloadImport')" icon="el-icon-download">下载模板
|
||||
</el-button>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="上传文件" placement="top">
|
||||
<el-card>
|
||||
<el-form>
|
||||
<el-upload
|
||||
action="#"
|
||||
name="file"
|
||||
ref="upload"
|
||||
:on-remove="
|
||||
(file, fileList) => {
|
||||
importData.fileList = fileHandleRemove(file, fileList)
|
||||
importResult = {
|
||||
errorCount: 0,
|
||||
successCount: 0,
|
||||
totalCount: 0,
|
||||
errorList: []
|
||||
}
|
||||
}
|
||||
"
|
||||
:on-change="
|
||||
(file, fileList) => {
|
||||
importData.fileList = fileHandleChange(file, fileList, { type: ['xls', 'xlsx'] })
|
||||
}
|
||||
"
|
||||
:auto-upload="false"
|
||||
:limit="1"
|
||||
:file-list="importData.fileList"
|
||||
>
|
||||
<el-button size="medium" type="" icon="el-icon-upload" style="width: 200px">选择文件</el-button>
|
||||
<div class="el-upload__tip" slot="tip" style="color: #f56c6c">只能上传 xls/xlsx 文件</div>
|
||||
</el-upload>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item placement="top" timestamp="导入结果">
|
||||
<el-card shadow="never">
|
||||
<p>总记录数:{{ errorInfoData.totalCount }}</p>
|
||||
<p>
|
||||
成功数:
|
||||
<span class="text-success">{{ errorInfoData.successCount }}</span>
|
||||
</p>
|
||||
<p>
|
||||
错误数:
|
||||
<span class="text-danger">{{ errorInfoData.errorCount }}</span>
|
||||
</p>
|
||||
<el-link @click="exportErrors" type="primary" v-if="errorInfoData.errorCount > 0">下载错误记录</el-link>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div style="text-align: right">
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="clearImportDialog" type="primary"
|
||||
:disabled="importLoading">取 消</el-button>
|
||||
<el-button type="primary" @click="clearSearchCnd"
|
||||
:loading="importLoading">清空查询条件</el-button>
|
||||
<el-button type="primary" @click="doImport" :loading="importLoading">核对人员</el-button>
|
||||
<el-button type="primary" @click="doImportSearch"
|
||||
:loading="importLoading">查询人员</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
exists_login_name_redis_key: {type: String, required: ""},
|
||||
group_id: {type: Number, required: ""},
|
||||
do_import_url: {type: String, required: ""}
|
||||
},
|
||||
mounted() {
|
||||
const s = document.createElement("script")
|
||||
s.type = "text/javascript"
|
||||
s.src = "/assets/platform/plugins/xlsx/xlsx.full.min.js"
|
||||
document.body.appendChild(s)
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
importData: {
|
||||
fileList: [],
|
||||
isFlag: false
|
||||
},
|
||||
errorInfoData: {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: 0
|
||||
},
|
||||
importLoading: false,
|
||||
doImportUrl:""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clearImportDialog() {
|
||||
this.$emit("clear_import_dialog")
|
||||
},
|
||||
clearSearchCnd() {
|
||||
this.importData = {
|
||||
fileList: [],
|
||||
}
|
||||
this.errorInfoData = {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: [],
|
||||
}
|
||||
|
||||
$.get('/platform/activity/basic/scope/clearSearchCnd', {existsLoginNameRedisKey: this.exists_login_name_redis_key}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$emit("flush", this.exists_login_name_redis_key)
|
||||
this.$message.success(res.msg)
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
resetImportData() {
|
||||
this.importData = {
|
||||
fileList: [],
|
||||
}
|
||||
this.errorInfoData = {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: 0
|
||||
}
|
||||
},
|
||||
doImportSearch() {
|
||||
this.$emit("flush", this.exists_login_name_redis_key)
|
||||
this.clearImportDialog()
|
||||
},
|
||||
doImport() {
|
||||
if (this.importData.fileList.length === 0) {
|
||||
this.$message.error({
|
||||
title: "错误",
|
||||
message: "请选择文件!"
|
||||
})
|
||||
return
|
||||
}
|
||||
const data = new FormData()
|
||||
data.append("groupId", this.group_id)
|
||||
this.importData.fileList.forEach((val) => {
|
||||
data.append("file", val.raw, val.raw.name)
|
||||
})
|
||||
this.importLoading = true
|
||||
this.$axios.post(this.do_import_url, data).then((res) => {
|
||||
if (res.code === 0) {
|
||||
if (res.data.errorList && res.data.errorList.length > 0) {
|
||||
this.$message.warning("核对失败")
|
||||
} else {
|
||||
this.$message.success("核对成功")
|
||||
}
|
||||
|
||||
this.errorInfoData = res.data
|
||||
this.$emit("flush", res.data.existsLoginNameRedisKey)
|
||||
} else {
|
||||
this.$message.warning("核对失败")
|
||||
}
|
||||
this.importLoading = false
|
||||
})
|
||||
},
|
||||
exportErrors() {
|
||||
const data = this.errorInfoData.errorList
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new()
|
||||
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.json_to_sheet(data)
|
||||
|
||||
// 将工作表添加到工作簿
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1")
|
||||
|
||||
// 将工作簿转换为二进制对象
|
||||
const excelBuffer = XLSX.write(workbook, {bookType: "xlsx", type: "array"})
|
||||
|
||||
// 将二进制对象转换为Blob对象
|
||||
const blob = new Blob([excelBuffer], {type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"})
|
||||
|
||||
// 创建下载链接并设置相关属性
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = "错误记录.xlsx"
|
||||
|
||||
// 模拟点击下载链接
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
// 清理下载链接
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
fileHandleRemove(file, fileList) {
|
||||
return fileList
|
||||
},
|
||||
fileHandleChange(file, fileList, {type, size}) {
|
||||
const removeFile = () => {
|
||||
fileList.splice(fileList.findIndex((v) => v === file))
|
||||
}
|
||||
|
||||
if (!file.size) {
|
||||
this.$message.warning("您选择的是空文件!")
|
||||
removeFile()
|
||||
}
|
||||
|
||||
if (type && type.length && !type.includes(file.name.split(".").pop().toLowerCase())) {
|
||||
this.$message.warning(`文件只能是 ${type.map((v) => v.toUpperCase()).join("/")} 格式!`)
|
||||
removeFile()
|
||||
}
|
||||
|
||||
if (size && !file.size < size) {
|
||||
this.$message.warning(`文件大小不能超过 ${size / 1024 / 1024}MB!`)
|
||||
removeFile()
|
||||
}
|
||||
|
||||
return fileList
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-card__body {
|
||||
padding: 25px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-drawer title="" size="78%" :visible.sync="userScopeDialog" :append-to-body="true" :with-header="false">
|
||||
<div style="padding: 0px 16px" class="clearfix">
|
||||
<el-button icon="el-icon-back" style="font-size: 16px" type="text" @click="userScopeDialog = false">返回</el-button>
|
||||
</div>
|
||||
<el-tabs tab-position="top" v-model="activeName" @tab-click="handleClick" style="margin: 20px">
|
||||
<el-tab-pane label="人员设置" name="1">
|
||||
<template>
|
||||
<user-scope ref="userScope" @group_change="group_change" :group_id.sync="groupId"></user-scope>
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="人员调整" name="2">
|
||||
<user-data-scope @group_change="group_change" ref="userDataScope"></user-data-scope>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-drawer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {},
|
||||
model: {},
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
groupId: "",
|
||||
userScopeDialog: false,
|
||||
activeName: "1"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick() {
|
||||
if (this.activeName === "2") {
|
||||
this.$refs.userDataScope.getActivityGroup()
|
||||
} else {
|
||||
this.$refs.userScope.getActivityGroup()
|
||||
}
|
||||
},
|
||||
group_change() {
|
||||
this.$emit("group_change")
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"user-data-scope": httpVueLoader("/components/module/activity/UserDataScope.vue"),
|
||||
"user-scope": httpVueLoader("/components/module/activity/UserScope.vue")
|
||||
},
|
||||
watch: {
|
||||
groupId: {
|
||||
async handler(newVal) {
|
||||
this.$emit("update:group_id", newVal)
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,339 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card shadow="never">
|
||||
<div class="search">
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">活动分组</div>
|
||||
<div class="search-item-option">
|
||||
<el-select
|
||||
placeholder="活动分组"
|
||||
v-model="pageForm.groupId"
|
||||
style="width: 100%"
|
||||
@change="
|
||||
doSearch()
|
||||
viewGroupName()
|
||||
"
|
||||
filterable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in activityGroupList"
|
||||
:label="item.groupName"
|
||||
:value="item.groupId"
|
||||
:key="item.groupId"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">姓名/工号</div>
|
||||
<div class="search-item-option">
|
||||
<el-input @keyup.enter.native="doSearch" clearable
|
||||
placeholder="请输入姓名或工号"
|
||||
v-model="pageForm.searchKeyword">
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-item" v-if="is_sysadmin || is_A06">
|
||||
<div class="search-item-label">所属工会</div>
|
||||
<div class="search-item-option">
|
||||
<el-select
|
||||
placeholder="所属工会"
|
||||
v-model="pageForm.unionId"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
@change="flushUnits"
|
||||
@clear="flushUnits"
|
||||
filterable
|
||||
>
|
||||
<el-option v-for="item in unions" :label="item.name" :value="item.id" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">所属单位</div>
|
||||
<div class="search-item-option">
|
||||
<el-select placeholder="所属单位" v-model="pageForm.unitId" style="width: 100%" clearable @change="doSearch"
|
||||
filterable>
|
||||
<el-option v-for="item in units" :label="item.name" :value="item.id" :key="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div class="search-item"
|
||||
v-if="${@shiro.hasRole('sysadmin')}">
|
||||
<div class="search-item-label">活动工会:</div>
|
||||
<div class="search-item-option">
|
||||
<el-select placeholder="所属工会" v-model="pageForm.activityUnionId"
|
||||
style="width: 100%;"
|
||||
clearable="true"
|
||||
@change="flushUnits" @clear="flushUnits"
|
||||
filterable="true">
|
||||
<el-option v-for="item in ActivityUnions" :label="item.unionname"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-item" v-if="${@shiro.hasRole('sysadmin')}">
|
||||
<div class="search-item-label">活动单位:</div>
|
||||
<div class="search-item-option">
|
||||
<el-select placeholder="所属单位" v-model="pageForm.activityUnitId"
|
||||
style="width: 100%;"
|
||||
clearable="true"
|
||||
@change="doSearch"
|
||||
filterable="true">
|
||||
<el-option v-for="item in ActivityUnits" :label="item.name"
|
||||
:value="item.id"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">教职工类别</div>
|
||||
<div class="search-item-option">
|
||||
<dict-select v-model="pageForm.personType" code="USER_PERSON_TYPE" @change="doSearch"
|
||||
style="width: 100%"></dict-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-item">
|
||||
<div class="search-item-label">在职状态</div>
|
||||
<div class="search-item-option">
|
||||
<dict-select
|
||||
v-model="pageForm.userState"
|
||||
style="width: 100%"
|
||||
clearable
|
||||
placeholder="在职状态"
|
||||
@change="doSearch"
|
||||
code="USER_STATE"
|
||||
></dict-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search-query">
|
||||
<el-button type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-card shadow="never" class="mt10">
|
||||
<table-tool :label="currentGroupName">
|
||||
<el-button v-if="is_sysadmin||is_A06"
|
||||
type="primary" size="medium" icon="el-icon-printer" @click="importDialogVisible=true">
|
||||
导入XLSX查询
|
||||
</el-button>
|
||||
<el-button @click="doExportUser" icon="el-icon-printer" size="small" type="primary"
|
||||
:disabled="!pageForm.groupId">
|
||||
导出活动分组人员xlsx
|
||||
</el-button>
|
||||
<el-button @click="doDelete(null)" type="danger" size="small" :disabled="tableData.length === 0">
|
||||
删除{{ currentGroupName }}
|
||||
</el-button>
|
||||
</table-tool>
|
||||
<el-table ref="userTable" :data="tableData" stripe border :size="tableSize" @sort-change="pageOrder">
|
||||
<el-table-column label="序号" type="index" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1 }}</span>
|
||||
</template>
|
||||
</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"
|
||||
:key="column.prop"
|
||||
:sortable="column.sortable"
|
||||
></el-table-column>
|
||||
<el-table-column align="center" header-align="center" label="操作" width="100">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button size="mini" type="danger" @click="doDelete(row.id)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container" style="margin-bottom: 0px">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-sizes="[5, 10, 20, 30, 50]"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, sizes, prev, pager, next"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
|
||||
<el-dialog :visible.sync="importDialogVisible" title="人员导入" width="45%" :append-to-body="true"
|
||||
:close-on-click-modal="false">
|
||||
<activity-import-user ref="importUserRef"
|
||||
@clear_import_dialog="clearImportDialog"
|
||||
@flush="flush"
|
||||
do_import_url="/platform/activity/basic/user/doImport"
|
||||
:group_id="pageForm.groupId"
|
||||
:exists_login_name_redis_key="pageForm.existsLoginNameRedisKey"></activity-import-user>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
group_id: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
is_H10() {
|
||||
return this.roleData.is_H10
|
||||
},
|
||||
is_A06() {
|
||||
return this.roleData.is_A06
|
||||
},
|
||||
is_H04() {
|
||||
return this.roleData.is_H04
|
||||
},
|
||||
is_H02() {
|
||||
return this.roleData.is_H02
|
||||
},
|
||||
is_H03() {
|
||||
return this.roleData.is_H03
|
||||
},
|
||||
is_sysadmin() {
|
||||
return this.roleData.is_sysadmin
|
||||
},
|
||||
unionid() {
|
||||
return this.roleData.unionid
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activityGroupList: [],
|
||||
ActivityUnions: [],
|
||||
ActivityUnits: [],
|
||||
unions: [],
|
||||
units: [],
|
||||
pageForm: {
|
||||
unionId: "",
|
||||
unitId: "",
|
||||
searchName: "username",
|
||||
personTypes: [],
|
||||
userStates: [],
|
||||
memberStatus: [],
|
||||
year: moment().format("YYYY")
|
||||
},
|
||||
tableColumns: [
|
||||
{prop: "loginName", label: "工号"},
|
||||
{prop: "userName", label: "姓名"},
|
||||
{prop: "sex", label: "性别", sortable: true},
|
||||
{prop: "birthday", label: "出生年月"},
|
||||
{prop: "mobile", label: "联系电话"},
|
||||
{prop: "personType", label: "教职工类别", sortable: true},
|
||||
{prop: "userState", label: "在职状态", sortable: true},
|
||||
{prop: "unitName", label: "所属单位", sortable: true},
|
||||
{prop: "unionName", label: "所属工会", sortable: true},
|
||||
{prop: "groupName", label: "所属分组", sortable: true}
|
||||
],
|
||||
roleData: {},
|
||||
currentGroupName: null,
|
||||
importDialogVisible: false,
|
||||
}
|
||||
},
|
||||
mixins: [initTableMixins],
|
||||
components: {
|
||||
"activity-import-user": httpVueLoader("/components/module/activity/ActivityImportUser.vue?v=" + new Date().getTime())
|
||||
},
|
||||
methods: {
|
||||
flush(exists_login_name_redis_key) {
|
||||
this.pageForm.existsLoginNameRedisKey = exists_login_name_redis_key
|
||||
this.doSearch()
|
||||
},
|
||||
clearImportDialog() {
|
||||
this.importDialogVisible = false
|
||||
},
|
||||
viewGroupName() {
|
||||
if (this.pageForm?.groupId) {
|
||||
const group = this.activityGroupList.find((v) => v.groupId === this.pageForm.groupId)
|
||||
this.currentGroupName = group.groupName + "人员"
|
||||
return
|
||||
}
|
||||
this.currentGroupName = "全部人员"
|
||||
},
|
||||
doExportUser() {
|
||||
window.open("/platform/activity/basic/user/doExportUser?groupId=" + this.pageForm.groupId)
|
||||
},
|
||||
doSearch2() {
|
||||
this.getActivityGroup()
|
||||
this.doSearch()
|
||||
},
|
||||
async doDelete(id, groupId) {
|
||||
const confirm = await this.$confirm("您确定要删除吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
let pageForm = clone(this.pageForm)
|
||||
pageForm.id = id
|
||||
const resp = await $.post("/platform/activity/basic/user/doDelete", pageForm)
|
||||
if (resp.code === 0) {
|
||||
this.$message.success(resp.msg)
|
||||
await this.getActivityGroup(this.pageForm.groupId)
|
||||
this.$emit("group_change")
|
||||
} else {
|
||||
}
|
||||
}
|
||||
},
|
||||
async flushUnits() {
|
||||
this.$set(this.pageForm, "unitId", "")
|
||||
if (this.is_A06 || this.is_sysadmin) {
|
||||
this.units = await this.$businessTool.listUnit(this.pageForm.unionId)
|
||||
// this.ActivityUnits = await getActivityUnits(this.pageForm.activityUnionId)
|
||||
} else {
|
||||
this.units = await this.$businessTool.listUnit(this.unionid)
|
||||
}
|
||||
},
|
||||
async getActivityGroup(id) {
|
||||
const resp = await $.get("/platform/activity/basic/scope/getActivityUserScopeGroup")
|
||||
this.activityGroupList = resp.data
|
||||
if (resp.data && resp.data.length > 0) {
|
||||
if (id) {
|
||||
if (this.activityGroupList.some((v) => v.groupId === id)) {
|
||||
this.$set(this.pageForm, "groupId", id)
|
||||
} else {
|
||||
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
|
||||
}
|
||||
} else {
|
||||
this.$set(this.pageForm, "groupId", resp.data[0].groupId)
|
||||
}
|
||||
}
|
||||
await this.doSearch()
|
||||
},
|
||||
async getRolesAndUnion() {
|
||||
const {data} = await $.post("/platform/activity/basic/scope/getRolesAndUnion")
|
||||
this.roleData = data
|
||||
},
|
||||
async pageData() {
|
||||
const resp = await $.post("/platform/activity/basic/user/pageData", this.pageForm)
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
}
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.unions = await this.$businessTool.listUnion()
|
||||
// this.ActivityUnions = await getActivityUnions()
|
||||
await this.flushUnits()
|
||||
await this.getActivityGroup()
|
||||
await this.getRolesAndUnion()
|
||||
this.viewGroupName()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,751 @@
|
||||
<template>
|
||||
<div class="user-scope">
|
||||
<template>
|
||||
<el-card shadow="never">
|
||||
<el-row class="query-row">
|
||||
<el-col class="query-title">姓名/工号:</el-col>
|
||||
<el-select
|
||||
clearable
|
||||
v-model="pageForm.userId"
|
||||
filterable
|
||||
remote
|
||||
collapse-tags
|
||||
placeholder="输入工号或者姓名查找"
|
||||
:remote-method="queryUser"
|
||||
@change="doSearch"
|
||||
style="width: 100%"
|
||||
multiple
|
||||
>
|
||||
<el-option v-for="o in userList" :label="o.username + '(' + o.loginname + ')'" :value="o.id"
|
||||
:key="o.id"></el-option>
|
||||
</el-select>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row" v-if="is_A06 === true || is_sysadmin === true || is_H02 === true">
|
||||
<el-col class="query-title hidden-xs-only">会员类型:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-tag
|
||||
:effect="pageForm.memberTypes.includes(item.name) ? 'dark' : 'plain'"
|
||||
:key="item.code"
|
||||
:type="item.name"
|
||||
@click="tagClick('memberTypes', item.name)"
|
||||
style="margin-right: 10px; cursor: pointer"
|
||||
v-for="item in memberTypeOptions"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-tag>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row">
|
||||
<el-col class="query-title hidden-xs-only">性别:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-tag
|
||||
:effect="pageForm.sexTypes.includes(item.name) ? 'dark' : 'plain'"
|
||||
:key="item.code"
|
||||
:type="item.name"
|
||||
@click="tagClick('sexTypes', item.name)"
|
||||
style="margin-right: 10px; cursor: pointer"
|
||||
v-for="item in sexTypeOptions"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-tag>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row" v-if="is_A06 === true || is_sysadmin === true || is_H04 === true">
|
||||
<el-col class="query-title">教职工类别:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-tag
|
||||
:effect="pageForm.personTypes.includes(item.name) ? 'dark' : 'plain'"
|
||||
:key="item.code"
|
||||
:type="item.name"
|
||||
@click="tagClick('personTypes', item.name)"
|
||||
style="margin-right: 10px; cursor: pointer"
|
||||
v-for="item in personTypeOptions"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-tag>
|
||||
|
||||
<el-link
|
||||
:underline="false"
|
||||
@click="
|
||||
pageForm.personTypes = []
|
||||
doSearch()
|
||||
"
|
||||
type="danger"
|
||||
v-if="personTypeOptions.length && pageForm.personTypes.length"
|
||||
>
|
||||
清空
|
||||
</el-link>
|
||||
|
||||
<el-link
|
||||
:underline="false"
|
||||
@click="
|
||||
pageForm.personTypes = personTypeOptions.map((p) => p.code)
|
||||
doSearch()
|
||||
"
|
||||
type="success"
|
||||
>
|
||||
全部
|
||||
</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row" v-if="is_A06 === true || is_sysadmin === true || is_H04 === true">
|
||||
<el-col class="query-title">在职状态:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-tag
|
||||
:effect="pageForm.userStates.includes(item.name) ? 'dark' : 'plain'"
|
||||
:key="item.code"
|
||||
:type="item.name"
|
||||
@click="tagClick('userStates', item.name)"
|
||||
style="margin-right: 10px; cursor: pointer"
|
||||
v-for="item in userStateOptions"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-tag>
|
||||
|
||||
<el-link
|
||||
:underline="false"
|
||||
@click="
|
||||
pageForm.userStates = []
|
||||
doSearch()
|
||||
"
|
||||
type="danger"
|
||||
v-if="userStateOptions.length && pageForm.userStates.length"
|
||||
>
|
||||
清空
|
||||
</el-link>
|
||||
<el-link
|
||||
:underline="false"
|
||||
@click="
|
||||
pageForm.userStates = userStateOptions.map((p) => p.code)
|
||||
doSearch()
|
||||
"
|
||||
type="success"
|
||||
>
|
||||
全部
|
||||
</el-link>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row" v-if="is_A06 === true || is_sysadmin === true || is_H04 === true">
|
||||
<el-col class="query-title">部门查询:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
|
||||
<el-select
|
||||
@change="
|
||||
flushUnits()
|
||||
doSearch()
|
||||
"
|
||||
@clear="
|
||||
flushUnits()
|
||||
doSearch()
|
||||
"
|
||||
:clearable="is_A06 === true || is_sysadmin === true"
|
||||
filterable
|
||||
placeholder="所属工会"
|
||||
style="width: 100%"
|
||||
v-model="pageForm.unionId"
|
||||
>
|
||||
<el-option :label="item.name" :value="item.id" :key="item.id" v-for="item in unions"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
|
||||
<el-select
|
||||
@change="doSearch"
|
||||
@clear="doSearch"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="所属单位"
|
||||
style="width: 100%"
|
||||
v-model="pageForm.unitId"
|
||||
>
|
||||
<el-option :label="item.name" :value="item.id" :key="item.id" v-for="item in units"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<template v-if="is_A06 === true || is_sysadmin === true">
|
||||
<!--<el-row class="query-row">
|
||||
<el-col class="query-title">活动部门:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
|
||||
<el-select @change="flushUnits();doSearch()" @clear="flushUnits();doSearch()"
|
||||
:clearable="is_A06===true||is_sysadmin===true"
|
||||
filterable="true"
|
||||
placeholder="所属工会" style="width: 100%"
|
||||
v-model="pageForm.activityUnionId">
|
||||
<el-option :label="item.unionname" :value="item.id"
|
||||
v-for="item in activityUnions"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :lg="12" :md="12" :sm="24" :xl="12" :xs="24">
|
||||
<el-select @change="doSearch" @clear="doSearch" clearable="true"
|
||||
filterable="true"
|
||||
placeholder="所属单位"
|
||||
style="width: 100%"
|
||||
v-model="pageForm.activityUnitId">
|
||||
<el-option :label="item.name" :value="item.id"
|
||||
v-for="item in activityUnits"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>-->
|
||||
|
||||
<el-row class="query-row">
|
||||
<el-col class="query-title">系统角色:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-select
|
||||
@change="doSearch" clearable filterable
|
||||
placeholder="请选择角色"
|
||||
multiple
|
||||
style="width: 100%"
|
||||
v-model="pageForm.roleIds">
|
||||
<el-option :label="item.name" :value="item.id"
|
||||
:key="item.id"
|
||||
v-for="item in roleList"></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-select placeholder="请选择教代会" v-model="pageForm.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-col>
|
||||
</el-row>
|
||||
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-row class="query-row"
|
||||
v-if="is_A06===true||is_sysadmin===true||is_H02===true">
|
||||
<el-col class="query-title">协会:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-select v-model="pageForm.clubId" placeholder="请选择协会" @change="doSearch"
|
||||
filterable
|
||||
:clearable="is_A06===true||is_sysadmin===true||(is_H04===true&&is_H02===true)"
|
||||
style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in clubOptions"
|
||||
:key="item.id"
|
||||
:label="item.clubName"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row">
|
||||
<el-col class="query-title">活动组别:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-select
|
||||
@change="doSearch()"
|
||||
placeholder="请选择活动组别范围之外的人员"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
v-model="pageForm.activityGroupId"
|
||||
>
|
||||
<el-option
|
||||
:label="item.groupName"
|
||||
:value="item.groupId"
|
||||
:key="item.groupId"
|
||||
v-for="item in activityGroupList2"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row class="query-row" v-if="is_A06 === true || is_sysadmin === true || is_H04 === true">
|
||||
<el-col class="query-title">年龄范围:</el-col>
|
||||
<el-col class="query-content">
|
||||
<el-row type="flex" style="align-items: center">
|
||||
<el-col :span="15">
|
||||
<el-slider v-model="pageForm.age" range show-stops :max="100"></el-slider>
|
||||
</el-col>
|
||||
<el-col :span="9" class="pl20">当前范围:{{ pageForm.age }}</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- <el-row class="query-row"
|
||||
v-if="is_H10===true||is_A06===true||is_sysadmin===true||is_H04===true">
|
||||
<el-col class="query-title">条件匹配:</el-col>
|
||||
<el-col class="query-content">
|
||||
<user-cnd
|
||||
@cnd="(v)=>{this.$set(this.pageForm,'activityUserCnd',v)}"></user-cnd>
|
||||
</el-col>
|
||||
</el-row>-->
|
||||
|
||||
<el-row class="query-row" style="justify-content: end">
|
||||
<el-checkbox v-model="pageForm.reverseSelection" label="是否反选" border @change="doSearch"
|
||||
class="reverseCheckBox"></el-checkbox>
|
||||
<el-button type="danger" icon="el-icon-circle-close" @click="doReset">重置</el-button>
|
||||
<el-button type="primary" icon="el-icon-search" @click="doSearch">搜索</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-card class="mt10" shadow="never">
|
||||
<table-tool label="筛选人员">
|
||||
<el-button v-if="is_sysadmin||is_A06"
|
||||
type="primary" size="medium" icon="el-icon-printer" @click="importDialogVisible=true">
|
||||
导入XLSX设置分组
|
||||
</el-button>
|
||||
<el-button @click="doExportUser" size="medium" type="primary" icon="el-icon-download">导出查询人员</el-button>
|
||||
<el-button @click="setDialogVisible = true" size="medium" type="primary">设置为活动人员</el-button>
|
||||
</table-tool>
|
||||
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder" border ref="userTable" stripe>
|
||||
<el-table-column label="序号" type="index" width="80">
|
||||
<template slot-scope="scope">
|
||||
<span>{{ scope.$index + (pageForm.pageNumber - 1) * pageForm.pageSize + 1 }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<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"
|
||||
></el-table-column>
|
||||
</el-table>
|
||||
<el-row class="el-pagination-container">
|
||||
<el-pagination
|
||||
@size-change="pageSizeChange"
|
||||
@current-change="pageNumberChange"
|
||||
:current-page="pageForm.pageNumber"
|
||||
:page-size="pageForm.pageSize"
|
||||
layout="total, prev, pager, next"
|
||||
:total="pageForm.totalCount"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</template>
|
||||
<el-dialog :visible.sync="setDialogVisible" title="设置活动人员" width="45%" :append-to-body="true"
|
||||
:close-on-click-modal="false">
|
||||
<el-form :model="formData" :rules="rules" label-width="100px" ref="setForm">
|
||||
<el-form-item label="添加方式" prop="setGroupType">
|
||||
<el-radio-group size="small" v-model="formData.setGroupType">
|
||||
<el-radio :label="1">添加至原有分组</el-radio>
|
||||
<el-radio :label="2">添加到新分组</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-row v-if="formData.setGroupType === 1">
|
||||
<el-form-item label="原有分组" prop="setGroupId">
|
||||
<el-radio-group size="small" v-model="formData.setGroupId">
|
||||
<el-radio
|
||||
:disabled="item.groupId != pageForm.activityGroupId"
|
||||
:label="item.groupId"
|
||||
:key="item.groupId"
|
||||
border
|
||||
v-for="item in activityGroupList"
|
||||
>
|
||||
{{ item.groupName }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row v-if="formData.setGroupType === 2">
|
||||
<el-form-item label="新分组" prop="setGroupName">
|
||||
<el-input placeholder="请输入新分组名称" v-model="formData.setGroupName"></el-input>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<span class="dialog-footer" slot="footer">
|
||||
<el-button @click="setDialogVisible = false">取 消</el-button>
|
||||
<el-button @click="doSetActivityUser" type="primary" v-loading="settingLoading">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :visible.sync="importDialogVisible" title="设置活动人员" width="45%" :append-to-body="true"
|
||||
:close-on-click-modal="false">
|
||||
<activity-import-user ref="importUserRef"
|
||||
@clear_import_dialog="clearImportDialog"
|
||||
@flush="flush"
|
||||
do_import_url="/platform/activity/basic/scope/doImport"
|
||||
:exists_login_name_redis_key="pageForm.existsLoginNameRedisKey"></activity-import-user>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {},
|
||||
model: {},
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
userList: [],
|
||||
clubOptions: [],
|
||||
settingLoading: false,
|
||||
setDialogVisible: false,
|
||||
setGroupType: null,
|
||||
setGroupId: null,
|
||||
setGroupName: null,
|
||||
personTypeOptions: [],
|
||||
userStateOptions: [],
|
||||
activityGroupList: [],
|
||||
activityGroupList2: [],
|
||||
memberTypeOptions: [
|
||||
{
|
||||
code: "unionMember",
|
||||
name: "工会会员"
|
||||
},
|
||||
{
|
||||
code: "welfareMember",
|
||||
name: "福利会员"
|
||||
},
|
||||
{
|
||||
code: "sickFundMember",
|
||||
name: "基金会员"
|
||||
}
|
||||
],
|
||||
sexTypeOptions: [
|
||||
{code: "男", name: "男"},
|
||||
{code: "女", name: "女"}
|
||||
],
|
||||
pageForm: {
|
||||
age: [0, 0],
|
||||
unionId: "",
|
||||
unitId: "",
|
||||
clubId: "",
|
||||
searchName: "u.username",
|
||||
personTypes: [],
|
||||
userStates: [],
|
||||
memberStatus: [],
|
||||
memberTypes: [],
|
||||
sexTypes: [],
|
||||
minAge: 0,
|
||||
maxAge: 0,
|
||||
year: moment().format("YYYY"),
|
||||
module: "",
|
||||
teacherMeetingId: "",
|
||||
activityGroupId: "",
|
||||
roleIds: [],
|
||||
userId: [],
|
||||
activityUserCnd: "",
|
||||
reverseSelection: false
|
||||
},
|
||||
activityUnions: [],
|
||||
unions: [],
|
||||
units: [],
|
||||
activityUnits: [],
|
||||
meetingOptions: [],
|
||||
roleList: [],
|
||||
tableHeight: "0px",
|
||||
tableColumns: [
|
||||
{prop: "loginName", label: "工号"},
|
||||
{prop: "userName", label: "姓名"},
|
||||
{prop: "sex", label: "性别"},
|
||||
{prop: "birthday", label: "出生年月", sortable: true},
|
||||
{prop: "age", label: "年龄", sortable: true},
|
||||
{prop: "mobile", label: "联系电话"},
|
||||
{prop: "personType", label: "教职工类别", sortable: true},
|
||||
{prop: "userState", label: "在职状态", sortable: true},
|
||||
{prop: "unitName", label: "所属单位", sortable: true},
|
||||
{prop: "unionName", label: "所属工会", sortable: true}
|
||||
// {prop: 'activityUnionName', label: '活动工会', sortable: true},
|
||||
],
|
||||
rules: {
|
||||
setGroupType: [{required: true, message: "请选择添加方式", trigger: ["blur", "change"]}],
|
||||
setGroupId: [{required: true, message: "请选择分组", trigger: ["blur", "change"]}],
|
||||
setGroupName: [{required: true, message: "请输入分组名称", trigger: ["blur", "change"]}]
|
||||
},
|
||||
relatedSessionMenus: ["aca4d14498c145ceb5b24ed70776aae2", "733d7266652740a3aeac9da97ab5eeca", "fe2d0768e26d4a80beeb159306bb8d01"],
|
||||
roleData: {},
|
||||
importDialogVisible: false,
|
||||
sessionOptions: [],
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
is_H10() {
|
||||
return this.roleData.is_H10
|
||||
},
|
||||
is_A06() {
|
||||
return this.roleData.is_A06
|
||||
},
|
||||
is_H04() {
|
||||
return this.roleData.is_H04
|
||||
},
|
||||
is_H02() {
|
||||
return this.roleData.is_H02
|
||||
},
|
||||
is_sysadmin() {
|
||||
return this.roleData.is_sysadmin
|
||||
},
|
||||
unionid() {
|
||||
return this.roleData.unionid
|
||||
}
|
||||
},
|
||||
components: {
|
||||
"user-cnd": httpVueLoader("/components/plugins/UserCnd.vue"),
|
||||
"activity-import-user": httpVueLoader("/components/module/activity/ActivityImportUser.vue?v=" + new Date().getTime())
|
||||
},
|
||||
methods: {
|
||||
listSession() {
|
||||
this.$axios.post("/platform/teacherCongress/common/listSession").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.sessionOptions = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
flush(exists_login_name_redis_key) {
|
||||
this.pageForm.existsLoginNameRedisKey = exists_login_name_redis_key
|
||||
this.doSearch()
|
||||
},
|
||||
clearImportDialog() {
|
||||
this.importDialogVisible = false
|
||||
},
|
||||
doExportUser() {
|
||||
let props = {}
|
||||
this.tableColumns.forEach((v) => {
|
||||
props[v.prop] = v.label
|
||||
})
|
||||
const pageForm = clone(this.pageForm)
|
||||
pageForm.personTypes = JSON.stringify(pageForm.personTypes)
|
||||
pageForm.userStates = JSON.stringify(pageForm.userStates)
|
||||
pageForm.memberStatus = JSON.stringify(pageForm.memberStatus)
|
||||
pageForm.memberTypes = JSON.stringify(pageForm.memberTypes)
|
||||
pageForm.sexTypes = JSON.stringify(pageForm.sexTypes)
|
||||
pageForm.roleIds = JSON.stringify(pageForm.roleIds)
|
||||
pageForm.userId = JSON.stringify(pageForm.userId)
|
||||
pageForm.age = JSON.stringify(pageForm.age)
|
||||
pageForm.props = JSON.stringify(props)
|
||||
pageForm.activityUserCnd = pageForm.activityUserCnd ? JSON.stringify(pageForm.activityUserCnd) : pageForm.activityUserCnd
|
||||
this.$downLoad("/platform/activity/basic/scope/doExportUser", {
|
||||
data: JSON.stringify(pageForm)
|
||||
})
|
||||
},
|
||||
doReset() {
|
||||
this.pageForm.userId = []
|
||||
this.pageForm.memberTypes = []
|
||||
this.pageForm.sexTypes = []
|
||||
this.pageForm.personTypes = []
|
||||
this.pageForm.userStates = []
|
||||
this.pageForm.unionId = ""
|
||||
this.pageForm.unitId = ""
|
||||
this.pageForm.module = ""
|
||||
this.pageForm.teacherMeetingId = ""
|
||||
this.pageForm.roleIds = []
|
||||
this.pageForm.clubId = ""
|
||||
this.pageForm.activityGroupId = ""
|
||||
this.pageForm.activityUserCnd = ""
|
||||
this.pageForm.age = [0, 0]
|
||||
this.pageForm.reverseSelection = false
|
||||
this.doSearch()
|
||||
},
|
||||
async queryUser(val) {
|
||||
const resp = await $.get("/open/common/userOptions", {query: val})
|
||||
this.userList = resp.data
|
||||
},
|
||||
tagClick(key, val) {
|
||||
let idx = this.pageForm[key].indexOf(val)
|
||||
if (idx !== -1) {
|
||||
this.pageForm[key].splice(idx, 1)
|
||||
} else {
|
||||
this.pageForm[key].push(val)
|
||||
}
|
||||
this.doSearch()
|
||||
},
|
||||
async flushUnits() {
|
||||
this.$set(this.pageForm, "unitId", "")
|
||||
if (this.is_sysadmin || this.is_A06) {
|
||||
this.units = await this.$businessTool.listUnit(this.pageForm.unionId)
|
||||
// this.activityUnits = await getActivityUnits(this.pageForm.activityUnionId)
|
||||
} else {
|
||||
this.units = await this.$businessTool.listUnit(this.unionId)
|
||||
}
|
||||
},
|
||||
async pageData() {
|
||||
const pageForm = clone(this.pageForm)
|
||||
pageForm.personTypes = JSON.stringify(pageForm.personTypes)
|
||||
pageForm.userStates = JSON.stringify(pageForm.userStates)
|
||||
pageForm.memberStatus = JSON.stringify(pageForm.memberStatus)
|
||||
pageForm.memberTypes = JSON.stringify(pageForm.memberTypes)
|
||||
pageForm.sexTypes = JSON.stringify(pageForm.sexTypes)
|
||||
pageForm.roleIds = JSON.stringify(pageForm.roleIds)
|
||||
pageForm.userId = JSON.stringify(pageForm.userId)
|
||||
pageForm.age = JSON.stringify(pageForm.age)
|
||||
pageForm.activityUserCnd = pageForm.activityUserCnd ? JSON.stringify(pageForm.activityUserCnd) : pageForm.activityUserCnd
|
||||
|
||||
const resp = await $.post("/platform/activity/basic/scope/pageData", {data: JSON.stringify(pageForm)})
|
||||
|
||||
if (resp.code === 0) {
|
||||
this.tableData = resp.data.list
|
||||
this.pageForm.totalCount = resp.data.totalCount
|
||||
} else {
|
||||
this.notifyWarning(resp.msg)
|
||||
}
|
||||
},
|
||||
async doSetActivityUser() {
|
||||
const valid = await this.$refs.setForm.validate()
|
||||
if (!valid) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.tableData.length === 0) {
|
||||
this.$message.warning("请先指定筛选条件!")
|
||||
return
|
||||
}
|
||||
const confirm = await this.$confirm("是否将符合搜索条件的用户设为活动人员?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "warning"
|
||||
})
|
||||
if (confirm === "confirm") {
|
||||
this.settingLoading = true
|
||||
const pageForm = clone(this.pageForm)
|
||||
pageForm.personTypes = JSON.stringify(pageForm.personTypes)
|
||||
pageForm.userStates = JSON.stringify(pageForm.userStates)
|
||||
pageForm.memberStatus = JSON.stringify(pageForm.memberStatus)
|
||||
pageForm.memberTypes = JSON.stringify(pageForm.memberTypes)
|
||||
pageForm.sexTypes = JSON.stringify(pageForm.sexTypes)
|
||||
pageForm.roleIds = JSON.stringify(pageForm.roleIds)
|
||||
pageForm.userId = JSON.stringify(pageForm.userId)
|
||||
pageForm.age = JSON.stringify(pageForm.age)
|
||||
pageForm.activityUserCnd = pageForm.activityUserCnd ? JSON.stringify(pageForm.activityUserCnd) : pageForm.activityUserCnd
|
||||
|
||||
Object.assign(pageForm, this.formData)
|
||||
const resp = await $.post("/platform/activity/basic/scope/doSetActivityUser", {data: JSON.stringify(pageForm)})
|
||||
if (resp.code === 0) {
|
||||
this.setDialogVisible = false
|
||||
// this.userScopeDialog = false
|
||||
await this.getActivityGroup()
|
||||
this.doSearch()
|
||||
this.$message.success(resp.msg)
|
||||
this.formData = {
|
||||
setGroupType: null,
|
||||
setGroupId: null,
|
||||
setGroupName: null
|
||||
}
|
||||
} else {
|
||||
this.$message.error(resp.msg)
|
||||
}
|
||||
this.settingLoading = false
|
||||
this.$emit("update:group_id", resp.data)
|
||||
this.$emit("group_change")
|
||||
}
|
||||
},
|
||||
async getActivityGroup() {
|
||||
const resp = await $.get("/platform/activity/basic/scope/getActivityUserScopeGroup")
|
||||
this.activityGroupList = resp.data
|
||||
this.activityGroupList2 = []
|
||||
resp.data.forEach((v) => {
|
||||
this.activityGroupList2.push({
|
||||
groupId: v.groupId,
|
||||
groupName: v.groupName + "范围之外人员"
|
||||
})
|
||||
})
|
||||
},
|
||||
async getRoleListByMenuId() {
|
||||
const {data} = await $.post("/platform/activity/basic/scope/getRoleListByMenuId")
|
||||
this.roleList = data
|
||||
},
|
||||
async getRolesAndUnion() {
|
||||
const {data} = await $.post("/platform/activity/basic/scope/getRolesAndUnion")
|
||||
this.roleData = data
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.listSession()
|
||||
await this.getRolesAndUnion()
|
||||
this.clubOptions = await this.$businessTool.listCLubByRole()
|
||||
if ((this.is_sysadmin || this.is_A06 || this.is_H02) === false && this.is_H04 === true) {
|
||||
this.unions = this.$businessTool.listUnion(this.unionid)
|
||||
this.$set(this.pageForm, "unionId", this.unions[0].id)
|
||||
} else {
|
||||
this.unions = await this.$businessTool.listUnion()
|
||||
}
|
||||
if (this.is_H04 === false && this.is_H02 === true) {
|
||||
if (this.clubOptions.length > 0) {
|
||||
this.$set(this.pageForm, "clubId", this.clubOptions[0].id)
|
||||
}
|
||||
}
|
||||
await this.flushUnits()
|
||||
await this.getActivityGroup()
|
||||
this.personTypeOptions = await this.$businessTool.getDictOptions("USER_PERSON_TYPE")
|
||||
this.userStateOptions = await this.$businessTool.getDictOptions("USER_STATE")
|
||||
await this.getRoleListByMenuId()
|
||||
await this.pageData()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.query-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.query-row:not(:last-child) {
|
||||
border-bottom: 1px dashed rgb(230, 230, 230);
|
||||
}
|
||||
|
||||
.query-row > .query-title {
|
||||
width: 100px;
|
||||
max-width: 100px;
|
||||
min-width: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.query-row > .query-content {
|
||||
min-width: 200px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.query-row > .query-content > .el-tag {
|
||||
margin-bottom: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 992px) {
|
||||
.query-row:nth-child(4) .query-content .el-col:not(:last-child) {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.query-title {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.header_card .el-card__body {
|
||||
padding: 2px 20px;
|
||||
}
|
||||
|
||||
.user_card .el-card__body {
|
||||
padding: 2px 20px;
|
||||
}
|
||||
|
||||
.user_card {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
.user_table {
|
||||
max-height: 260px;
|
||||
}
|
||||
|
||||
.user_table .el-table__cell {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.reverseCheckBox {
|
||||
margin: 0 10px 0 0 !important;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,145 @@
|
||||
<template>
|
||||
<el-table :data="tableData" style="width: 100%;height: 100%" stripe border
|
||||
ref="MaleFemaleTab"
|
||||
:header-cell-style="{background:'#FAFAFA'}" row-key="id" @sort-change="pageOrder"
|
||||
v-loading="tableLoading" size="mini" fixed>
|
||||
|
||||
<el-table-column align="center" header-align="center" type="index" :index="indexMethod" label="序号"
|
||||
width="80px" fixed></el-table-column>
|
||||
<el-table-column label="分工会" prop="unionname" header-align="center"
|
||||
align="center" fixed show-overflow-tooltip width="250px"></el-table-column>
|
||||
<el-table-column label="甲组" header-align="center" v-if="tableColumns.length>0">
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="乙组" header-align="center" v-if="tableColumns2.length>0">
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns2"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="丙组" header-align="center" v-if="tableColumns3.length>0">
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns3"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="丁组" header-align="center" v-if="tableColumns4.length>0">
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns4"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="团体" header-align="center" v-if="tableColumns5.length>0">
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns5"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="总分" prop="totalScore" header-align="center"
|
||||
align="center" show-overflow-tooltip fixed="right"></el-table-column>
|
||||
|
||||
<el-table-column align="center" header-align="center" type="index" show-overflow-tooltip label="名次"
|
||||
width="80px" fixed="right"></el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
Form: {
|
||||
type: Object,
|
||||
default: {},
|
||||
}
|
||||
},
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
maxHeight: 0,
|
||||
tableColumns2: [],
|
||||
tableColumns3: [],
|
||||
tableColumns4: [],
|
||||
tableColumns5: []
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/* const tabHeight = document.getElementById("app").clientHeight - (this.$refs.MaleFemaleTab.$el.offsetTop + 60)
|
||||
this.$nextTick(()=>{
|
||||
this.maxHeight = tabHeight
|
||||
})*/
|
||||
},
|
||||
methods: {
|
||||
async isMaleFemale() {
|
||||
|
||||
this.tableColumns = []
|
||||
this.tableColumns2 = []
|
||||
this.tableColumns3 = []
|
||||
this.tableColumns4 = []
|
||||
this.tableColumns5 = []
|
||||
this.tableData = []
|
||||
const {data} = await this.$axios.post("/platform/activity/score/statistics/isMaleFemale", this.Form)
|
||||
data.eventList.forEach(v => {
|
||||
if (v.baname == "甲组" || v.baname == "乙组" || v.baname == "丙组" || v.baname == "丁组") {
|
||||
this.tableColumns.push({label: v.isMenWomen ? v.label.substr(4) : v.label.substr(2), prop: v.label})
|
||||
} else {
|
||||
|
||||
this.tableColumns5.push({label: v.label, prop: v.label})
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
this.tableData = data.score.sort(this.compare("totalScore"))
|
||||
if (this.Form.unionname) {
|
||||
this.tableData = this.tableData.filter(v => v.unionname == this.Form.unionname)
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
|
||||
compare(prop) {
|
||||
return function (obj1, obj2) {
|
||||
const val1 = obj1[prop];
|
||||
const val2 = obj2[prop];
|
||||
if (val1 > val2) {
|
||||
return -1;
|
||||
} else if (val1 < val2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,71 @@
|
||||
<template>
|
||||
<el-table :data="tableData" style="width: 100%;height: 100%" stripe border
|
||||
ref="MaleFemaleTab"
|
||||
:header-cell-style="{background:'#FAFAFA'}" row-key="id" @sort-change="pageOrder"
|
||||
v-loading="tableLoading" size="mini" fixed >
|
||||
<!--:max-height="maxHeight"-->
|
||||
<el-table-column align="center" header-align="center" type="index" :index="indexMethod" label="序号"
|
||||
width="80px" fixed></el-table-column>
|
||||
<el-table-column label="分工会" prop="unionname" header-align="center"
|
||||
align="center" fixed show-overflow-tooltip></el-table-column>
|
||||
|
||||
<el-table-column label="总分" prop="totalScore" header-align="center"
|
||||
align="center" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column align="center" header-align="center" type="index" :index="indexMethod" label="名次"
|
||||
width="80px" ></el-table-column>
|
||||
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
Form: {
|
||||
type: Object,
|
||||
default: {},
|
||||
}
|
||||
},
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
maxHeight: 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/*const tabHeight = document.getElementById("app").clientHeight - (this.$refs.MaleFemaleTab.$el.offsetTop + 60)
|
||||
this.$nextTick(() => {
|
||||
this.maxHeight = tabHeight
|
||||
})*/
|
||||
},
|
||||
methods: {
|
||||
async isScoreTopEight() {
|
||||
|
||||
this.tableColumns = []
|
||||
this.tableData = []
|
||||
const {data} = await this.$axios.post(loc() + "/isScoreTopEight", this.Form)
|
||||
const table = data.score.sort(this.compare("totalScore"))
|
||||
this.tableData = table.slice(0, 8)
|
||||
|
||||
|
||||
|
||||
},
|
||||
|
||||
compare(prop) {
|
||||
return function (obj1, obj2) {
|
||||
const val1 = obj1[prop];
|
||||
const val2 = obj2[prop];
|
||||
if (val1 > val2) {
|
||||
return -1;
|
||||
} else if (val1 < val2) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- <el-table :data="tableData" style="width: 100%" stripe border
|
||||
:header-cell-style="{background:'#FAFAFA'}" row-key="id" @sort-change="pageOrder"
|
||||
v-loading="tableLoading" size="mini">
|
||||
|
||||
<el-table-column align="center" header-align="center" type="index" :index="indexMethod" label="名次"
|
||||
width="100px"></el-table-column>
|
||||
|
||||
<el-table-column
|
||||
align="center"
|
||||
header-align="center"
|
||||
v-for="column in tableColumns"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
:sortable="column.sortable"
|
||||
show-overflow-tooltip>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>-->
|
||||
<table class="table table-bordered" style="table-layout: fixed;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="text-align: center!important;">项目</th>
|
||||
<th style="text-align: center!important;" v-for="i in 8">第{{ i }}名</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="i in tableData">
|
||||
<td align="center" width="20%">{{ i.label }}</td>
|
||||
<td v-for="x in 8" align="center" width="10%">
|
||||
{{ getTableTdContent(i.sss, x) }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
Form: {
|
||||
type: Object,
|
||||
default: {},
|
||||
}
|
||||
},
|
||||
mixins: [initTableMixins],
|
||||
data() {
|
||||
return {
|
||||
tableData: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getTableTdContent(d, i) {
|
||||
return d.filter(v => {
|
||||
if (v.ranking == i) {
|
||||
return v.username
|
||||
}
|
||||
}).map(v => {
|
||||
return v.username
|
||||
}).toString()
|
||||
},
|
||||
async isTopEight() {
|
||||
var loading = this.$loading({
|
||||
lock: true,
|
||||
text: '数据正在查询中,请稍后...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
this.tableColumns = []
|
||||
this.tableData = []
|
||||
const {data} = await this.$axios.post(loc() + "/isTopEight", this.Form)
|
||||
data.eventList.map(v => {
|
||||
v.sss = []
|
||||
data.userList.map(x => {
|
||||
if (v.label === x.allname) {
|
||||
console.log(x)
|
||||
v.sss.push(x)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
this.tableData = data.eventList
|
||||
loading.close();
|
||||
},
|
||||
|
||||
|
||||
},
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog title="自动计算得分" :visible.sync="show_modal" :close-on-click-modal="false">
|
||||
<el-table :data="tableData" v-loading="loading" element-loading-text="智能查分中"
|
||||
element-loading-spinner="el-icon-loading text-primary"
|
||||
element-loading-background="rgba(0, 0, 0, 0.8)">
|
||||
<el-table-column type="index"></el-table-column>
|
||||
<el-table-column v-for="column in tableColumn"
|
||||
:key="column.prop"
|
||||
:label="column.label"
|
||||
:prop="column.prop"
|
||||
show-overflow-tooltip
|
||||
header-align="center"
|
||||
align="center">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<template #footer>
|
||||
<el-button @click="closeCalcTable">取 消</el-button>
|
||||
<el-button type="primary" @click="autoFillTab" :disabled="loading">自动计算填入</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
relation_year: {
|
||||
type: Object,
|
||||
default: {}
|
||||
},
|
||||
show_modal: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
tableData: [],
|
||||
tableColumn: [],
|
||||
proposalTableColumn: [
|
||||
{label: '提案编号', prop: 'proposalCode'},
|
||||
{label: '提案名称', prop: 'proposalName'},
|
||||
{label: '提案人', prop: 'username'},
|
||||
{label: '提案时间', prop: 'createTime'},
|
||||
{label: '教代会届次', prop: 'jdhallname'}
|
||||
],
|
||||
secTeaMeetTableColumn: [
|
||||
{label: '会议名称', prop: 'meeting_name'},
|
||||
{label: '会议时间', prop: 'meeting_time'},
|
||||
{label: '届次', prop: 'jdhallname'},
|
||||
{label: '申请人', prop: 'username'},
|
||||
],
|
||||
ConTableColumn: [
|
||||
{label: '被慰问/补助人', prop: 'be_username'},
|
||||
{label: '被慰问/补助人', prop: 'be_loginname'},
|
||||
{label: '申请时间', prop: 'apply_time'},
|
||||
{label: '慰问/补助类型', prop: 'typename'},
|
||||
{label: '类型', prop: 'querytype'},
|
||||
],
|
||||
PerformanceTableColumn: [
|
||||
{label: '获奖活动', prop: 'activityName'},
|
||||
{label: '获奖项目', prop: 'eventName'},
|
||||
{label: '获奖名次', prop: 'ranking'},
|
||||
{label: '获奖积分', prop: 'integral'},
|
||||
{label: '参加人数', prop: 'numberOfPeople'},
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
relation_year: {
|
||||
async handler(v) {
|
||||
switch (v.bz_relation_id) {
|
||||
case 'proposal':
|
||||
this.tableColumn = this.proposalTableColumn
|
||||
break
|
||||
case 'secteameet':
|
||||
this.tableColumn = this.secTeaMeetTableColumn
|
||||
break
|
||||
case 'ConandDif':
|
||||
this.tableColumn = this.ConTableColumn
|
||||
break
|
||||
case 'Performance':
|
||||
this.tableColumn = this.PerformanceTableColumn
|
||||
break
|
||||
default:
|
||||
this.tableColumn = []
|
||||
}
|
||||
|
||||
this.tableData = []
|
||||
this.loading = true
|
||||
const res = await $.get('/platform/TradeUnionAssessmentCalcScore/CalcScore', v)
|
||||
if (res.code === 0) {
|
||||
setTimeout(() => {
|
||||
this.loading = false
|
||||
this.tableData = res.data == null ? [] : res.data
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
immediate: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
autoFillTab() {
|
||||
// this.$emit('get_score_item_num', this.tableData.length)
|
||||
this.$emit('get_table_data', this.tableData)
|
||||
this.$emit('update:show_modal', false)
|
||||
},
|
||||
closeCalcTable() {
|
||||
this.$emit('update:show_modal', false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/**
|
||||
修改el-dialog样式
|
||||
*/
|
||||
.el-dialog__wrapper {
|
||||
overflow: unset;
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
margin-top: 5vh !important;
|
||||
/*height: 90vh;*/
|
||||
max-height: 90vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-dialog .el-dialog__header {
|
||||
height: 54px;
|
||||
max-height: 54px;
|
||||
}
|
||||
|
||||
/**
|
||||
header + footer
|
||||
(54 + 70) = 124px
|
||||
*/
|
||||
.el-dialog .el-dialog__body {
|
||||
overflow-y: auto !important;
|
||||
max-height: calc(90vh - 124px) !important;
|
||||
}
|
||||
|
||||
.el-dialog .el-dialog__footer {
|
||||
height: 70px;
|
||||
max-height: 70px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,423 @@
|
||||
<template>
|
||||
|
||||
<el-form class="el_form" :model="formData" ref="addForm" :rules="formRules" label-width="100px">
|
||||
<table-tool label="指标信息"></table-tool>
|
||||
<template v-if="!is_view">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item prop="annual" label="年  度">
|
||||
<el-date-picker
|
||||
style="width: 100%"
|
||||
:picker-options="pickerOptions"
|
||||
v-model="formData.annual"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="请选择年度">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item prop="assessment" label="考核名称">
|
||||
<el-input maxlength="30" v-model="formData.assessment" placeholder="请输入考核名称" type="text"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item prop="assessment" label="">
|
||||
<el-checkbox v-model="formData.isEvaluationIndex">是否采用往年考核指标</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item prop="zbId" label="往年指标" v-if="formData.isEvaluationIndex">
|
||||
<el-select v-model="formData.zbId" placeholder="请选择指标" filterable clearable
|
||||
style="width: 100%" @change="getByZbData">
|
||||
<el-option
|
||||
v-for="item in zbList"
|
||||
:key="item.id"
|
||||
:label="item.annual+item.assessment"
|
||||
: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 prop="fillTime" label="填报时间">
|
||||
<el-date-picker
|
||||
style="width: 100%"
|
||||
v-model="formData.fillTimeRange"
|
||||
type="datetimerange"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
start-placeholder="填报开始时间"
|
||||
end-placeholder="填报结束时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="appealTime" label="申诉时间">
|
||||
<el-date-picker
|
||||
style="width: 100%"
|
||||
v-model="formData.appealTimeRange"
|
||||
type="datetimerange"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
start-placeholder="申诉开始时间"
|
||||
end-placeholder="申诉结束时间">
|
||||
</el-date-picker>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<el-descriptions border class="descriptions-form" v-if="is_view" :column="2">
|
||||
<el-descriptions-item label="年度">{{ formData.annual }}</el-descriptions-item>
|
||||
<el-descriptions-item label="考核名称">{{ formData.assessment }}</el-descriptions-item>
|
||||
<el-descriptions-item label="填报时间">{{ formData.startDateTime }} 至 {{ formData.endDateTime }}</el-descriptions-item>
|
||||
<el-descriptions-item label="申诉时间">{{ formData.startApplyTime }} 至 {{ formData.endApplyTime }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<table-tool label="考核内容" class="mt10"></table-tool>
|
||||
|
||||
<el-form-item label="" label-width="0" v-for="(nr,idx) in formData.nrs" :key="idx">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<div style="width: 80%">
|
||||
<el-input v-model="nr.content" :disabled="is_view" maxlength="30" style="width: 68%" placeholder="请填写考核内容">
|
||||
<template slot="prepend"><span>{{ idx + 1 }}</span></template>
|
||||
</el-input>
|
||||
<el-select v-model="nr.auditUser" :disabled="is_view" placeholder="请根据姓名或工号选择审核人" style="width: 300px"
|
||||
remote reserve-keyword :remote-method="selectUser"
|
||||
clearable filterable>
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.loginName"
|
||||
:label="item.userName + item.loginName + '(' + item.unitName + ')'"
|
||||
:value="item.loginName">
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
<el-button v-if="!is_view" size="small" icon="el-icon-plus" type="primary"
|
||||
@click="formData.nrs.push({bzs:[{}], auditUser: ''})">
|
||||
添加内容
|
||||
</el-button>
|
||||
<el-button v-if="!is_view" size="small" :disabled="formData.nrs.length<=1" type="danger"
|
||||
@click="formData.nrs.splice(idx,1)" icon="el-icon-delete">
|
||||
</el-button>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
|
||||
|
||||
<el-row style="margin-top: 20px">
|
||||
<el-table :data="nr.bzs" style="width: 100%" border show-summary :summary-method="getSummaries" size="small">
|
||||
<el-table-column label="序号" type="index" width="50px">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="考核标准" prop="inspection" >
|
||||
<template v-slot="{row}">
|
||||
<el-input v-if="!is_view" v-model="row.inspection" placeholder="请填写考核标准"></el-input>
|
||||
<span v-else>{{ row.inspection }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="单项分" prop="single_score" width="200px">
|
||||
<template v-slot="{row}">
|
||||
<el-input v-if="!is_view" v-model="row.single_score" placeholder="请填写单项分"></el-input>
|
||||
<span v-else>{{ row.single_score }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="标准分" prop="score" width="200px">
|
||||
<template v-slot="{row}">
|
||||
<el-input v-if="!is_view" v-model.number="row.score" maxlength="4" placeholder="请填写标准分"></el-input>
|
||||
<span v-else>{{ row.score }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="考核验收方式" prop="remark" width="300px">
|
||||
<template v-slot="{row}">
|
||||
<el-input v-if="!is_view" v-model.number="row.remark" placeholder="请填写考核验收方式"></el-input>
|
||||
<span v-else>{{ row.remark }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="能否超过标准分" prop="beyond_highestscore" width="120px">
|
||||
<template v-slot="{row}">
|
||||
<el-switch
|
||||
v-if="!is_view"
|
||||
v-model="row.beyond_highestscore"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
<el-switch
|
||||
v-else
|
||||
disabled
|
||||
v-model="row.beyond_highestscore">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column label="是否提供佐证材料" prop="isEvidence" width="120px">
|
||||
<template v-slot="{row}">
|
||||
<el-switch
|
||||
v-if="!is_view"
|
||||
v-model="row.isEvidence"
|
||||
active-color="#13ce66"
|
||||
inactive-color="#ff4949">
|
||||
</el-switch>
|
||||
<el-switch
|
||||
v-else
|
||||
disabled
|
||||
v-model="row.isEvidence">
|
||||
</el-switch>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="关联模块" prop="associatedmodule" width="200px">-->
|
||||
<!-- <template v-slot="{row}">-->
|
||||
<!-- <el-input v-if="!is_view" v-model="row.associatedmodule" maxlength="10" placeholder="请填写关联模块"></el-input>-->
|
||||
<!-- <span v-else>{{ row.associatedmodule }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<!-- <el-table-column label="关联标识" prop="bz_relation_id" width="100px">-->
|
||||
<!-- <template v-slot="{row}">-->
|
||||
<!-- <el-input v-if="!is_view" v-model="row.bz_relation_id" maxlength="20" placeholder="请填写关联标识,该标识唯一"></el-input>-->
|
||||
<!-- <span v-else>{{ row.bz_relation_id }}</span>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<!-- <el-table-column label="能否删除" prop="canDelete" width="100px">-->
|
||||
<!-- <template v-slot="{row}">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- v-if="!is_view"-->
|
||||
<!-- :disabled="row.canDelete==false"-->
|
||||
<!-- v-model="row.canDelete"-->
|
||||
<!-- active-color="#13ce66"-->
|
||||
<!-- inactive-color="#ff4949">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- <el-switch-->
|
||||
<!-- v-else-->
|
||||
<!-- :disabled="!row.canDelete && row.id"-->
|
||||
<!-- v-model="row.canDelete">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
<el-table-column v-if="!is_view" label="操作" width="100px">
|
||||
<template v-slot="{row,$index}">
|
||||
<el-button size="mini" :disabled="nr.bzs.length<=1 || (!row.canDelete && row.id !== null)" type="danger" icon="el-icon-delete"
|
||||
@click="nr.bzs.splice($index,1)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<el-button v-if="!is_view" type="primary" plain style="width: 100%" size="mini" icon="el-icon-plus"
|
||||
@click="nr.bzs.push({})">
|
||||
添加考核标准
|
||||
</el-button>
|
||||
|
||||
<el-divider></el-divider>
|
||||
</el-row>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
const METHOD_NAME = "change"
|
||||
|
||||
module.exports = {
|
||||
props: {
|
||||
is_view: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
form_data: {
|
||||
type: Object,
|
||||
default: function() {
|
||||
return {
|
||||
nrs: [{
|
||||
bzs: [{}],
|
||||
auditUser: '',
|
||||
}],
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
formData: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.$emit(METHOD_NAME, val)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEvaluationIndex: false,
|
||||
zbList: [],
|
||||
formRules: {
|
||||
xxx: [{required: true, message: '请填写年度', trigger: ['blur', 'change']}],
|
||||
},
|
||||
pickerOptions: {
|
||||
disabledDate(time) {
|
||||
return (
|
||||
time.getFullYear() > new Date().getFullYear()
|
||||
);
|
||||
}
|
||||
},
|
||||
relationData:[
|
||||
{moduleName:'提案系统模块',id:'proposal'},
|
||||
{moduleName:'二级教代会',id:'secteameet'},
|
||||
{moduleName:'慰问/补助',id:'ConandDif'},
|
||||
],
|
||||
formData:{},
|
||||
userOptions: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async selectUser(query) {
|
||||
if (query) {
|
||||
this.userOptions = []
|
||||
this.userOptions = await this.getUserList(query)
|
||||
}
|
||||
},
|
||||
async getUserList(query) {
|
||||
const resp = await this.$axios.post("/platform/ghkh/Xjkhzb/getUserByKeyWord", {
|
||||
keyWord: query,
|
||||
})
|
||||
return resp.data
|
||||
},
|
||||
async getByZbData() {
|
||||
this.$axios.post('/platform/ghkh/khzb/edit', {id: this.formData.zbId}).then(res => {
|
||||
if (res.code === 0) {
|
||||
this.$set(this.formData, "nrs", res.data.nrs)
|
||||
}
|
||||
})
|
||||
},
|
||||
async getZbList() {
|
||||
this.$axios.post("/platform/ghkh/Xjkhzb/getZbList").then(res => {
|
||||
if (res.code === 0) {
|
||||
this.zbList = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
async getKh() {
|
||||
this.$axios.post("/platform/ghkh/khph/getKh", {"annual": this.formData.annual}).then(res => {
|
||||
if (res.code === 0) {
|
||||
if (res.data.length != 0) {
|
||||
this.kh = res.data
|
||||
this.data.khid = this.kh[0].id
|
||||
} else {
|
||||
this.kh = []
|
||||
this.pageForm.khid = ""
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
async khSearch() {
|
||||
await this.getKh()
|
||||
},
|
||||
getSummaries(param) {
|
||||
const {columns, data} = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '小计';
|
||||
return;
|
||||
} else if ([1,4,5,6,7].includes(index)) {
|
||||
sums[index] = '';
|
||||
return;
|
||||
}
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] += ' 分';
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.getZbList()
|
||||
if (!this.form_data || !Object.keys(this.form_data).length) {
|
||||
this.formData = {
|
||||
nrs: [{
|
||||
bzs: [{}],
|
||||
auditUser: '',
|
||||
}],
|
||||
}
|
||||
} else {
|
||||
this.formData = this.form_data
|
||||
// 初始化时间范围数据用于编辑
|
||||
if (this.formData.startDateTime && this.formData.endDateTime) {
|
||||
this.$set(this.formData, 'fillTimeRange', [this.formData.startDateTime, this.formData.endDateTime]);
|
||||
}
|
||||
if (this.formData.startApplyTime && this.formData.endApplyTime) {
|
||||
this.$set(this.formData, 'appealTimeRange', [this.formData.startApplyTime, this.formData.endApplyTime]);
|
||||
}
|
||||
// 回显审核人
|
||||
if (this.formData.nrs.length > 0) {
|
||||
const users = [...new Set(
|
||||
this.formData.nrs
|
||||
.map(o => o.auditUser)
|
||||
.filter(o => o != null && o !== '')
|
||||
)]
|
||||
let array = []
|
||||
for (const o of users) {
|
||||
array = array.concat(await this.getUserList(o))
|
||||
}
|
||||
this.userOptions = array
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.fixedBox {
|
||||
position: fixed;
|
||||
top: 200px;
|
||||
right: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
background-color: #409EFF;
|
||||
}
|
||||
|
||||
.el_form {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.el-input-group__prepend {
|
||||
background-color: #419BF8;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.el-descriptions-item__content {
|
||||
width: 300px;
|
||||
min-width: 300px;
|
||||
}
|
||||
|
||||
.el-descriptions-item__label.is-bordered-label {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<el-form :model="data" ref="form" :rules="formRules" label-width="100px">
|
||||
|
||||
<el-form-item prop="nr" label="考核内容">
|
||||
<el-input disabled v-model="data.nr" type="text"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="bz" label="考核标准">
|
||||
<el-input disabled v-model="data.bz" type="textarea" :autosize="{ minRows: 2, maxRows: 10}"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="remark" label="考核验收方式">
|
||||
<el-input disabled v-model="data.remark" type="textarea" :autosize="{ minRows: 2, maxRows: 10}"
|
||||
placeholder="暂无考核验收方式"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="clnr" label="材料说明">
|
||||
<el-input v-model="data.clnr" type="textarea" :autosize="{ minRows: 2, maxRows: 10}"
|
||||
placeholder="暂无材料说明" :disabled="is_view"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="佐证材料">
|
||||
<template v-if="is_view">
|
||||
<span v-if="!data.files">暂无佐证材料</span>
|
||||
<file-preview v-else :files="data.files" complete_result></file-preview>
|
||||
</template>
|
||||
<template v-else>
|
||||
<file-upload
|
||||
:value.sync="data.files"
|
||||
:upload_number="50"
|
||||
upload_mode="drag"
|
||||
upload_result_category="array"
|
||||
complete_result
|
||||
:accept="'.pdf,.doc,.docx,.jpg,.png'"
|
||||
></file-upload>
|
||||
</template>
|
||||
</el-form-item>
|
||||
|
||||
<slot name="extra"></slot>
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
<script>
|
||||
|
||||
const METHOD_NAME = "change"
|
||||
|
||||
module.exports = {
|
||||
props: {
|
||||
is_view: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
width: "200px",
|
||||
data: {
|
||||
type: Object,
|
||||
default: {
|
||||
files: () => [],
|
||||
}
|
||||
},
|
||||
},
|
||||
// components: {
|
||||
// 'file-upload': httpVueLoader('/components/plugins/FileUpload.vue')
|
||||
// },
|
||||
data() {
|
||||
return {
|
||||
formRules: {
|
||||
xxx: [{required: true, message: '', trigger: ['blur', 'change']}]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {},
|
||||
watch: {},
|
||||
created() {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,282 @@
|
||||
<template>
|
||||
<el-form class="el_form" :model="data" ref="addForm" :rules="formRules" label-width="100px">
|
||||
<vi-title title="指标信息"></vi-title>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="annual" label="年  度">
|
||||
<el-date-picker
|
||||
style="width: 100%"
|
||||
v-if="!is_view"
|
||||
:picker-options="pickerOptions"
|
||||
v-model="data.annual"
|
||||
type="year"
|
||||
value-format="yyyy"
|
||||
placeholder="选择年度">
|
||||
</el-date-picker>
|
||||
<el-input v-else style="color: #F6F7FA;" disabled v-model="data.annual"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="assessment" label="考核名称">
|
||||
<el-input maxlength="30" v-if="!is_view" v-model="data.assessment" placeholder="请填写考核名称"
|
||||
type="text">
|
||||
</el-input>
|
||||
<el-input v-else style="color: #F6F7FA;" disabled v-model="data.assessment"></el-input>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="assessment" label="" v-if="!is_view">
|
||||
<el-checkbox v-model="data.isEvaluationIndex">是否采用往年考核指标</el-checkbox>
|
||||
</el-form-item>
|
||||
<el-form-item prop="zbId" label="往年指标" v-show="data.isEvaluationIndex">
|
||||
<el-select v-model="data.zbId" placeholder="请选择指标" filterable clearable
|
||||
style="width: 100%" @change="getByZbData">
|
||||
<el-option
|
||||
v-for="item in zbList"
|
||||
:key="item.id"
|
||||
:label="item.annual+item.assessment"
|
||||
:value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<vi-title2 title="考核项目">
|
||||
<template #func>
|
||||
<el-button v-if="!is_view" size="small" style="float: right" icon="el-icon-plus" type="primary"
|
||||
@click="data.nrs.push({bzs:[{}]})">
|
||||
添加内容
|
||||
</el-button>
|
||||
</template>
|
||||
</vi-title2>
|
||||
|
||||
<el-form-item label="" label-width="0" v-for="nr,idx in data.nrs">
|
||||
|
||||
<el-row type="flex" align="middle" justify="space-between">
|
||||
<el-input v-model="nr.content" v-if="!is_view" maxlength="30" style="width: 50%" placeholder="请填写考核项目">
|
||||
<template slot="prepend"><span v-model="nr.contentNumber">{{ nr.contentNumber = idx + 1 }}</span></template>
|
||||
</el-input>
|
||||
<span style="background-color: #F6F7FA;" v-else>{{ idx + 1 }} {{ nr.content }}</span>
|
||||
<el-button v-if="!is_view" size="medium" :disabled="data.nrs.length<=1" type="danger"
|
||||
@click="data.nrs.splice(idx,1)"
|
||||
icon="el-icon-delete"></el-button>
|
||||
</el-row>
|
||||
|
||||
<el-row style="margin-top: 20px">
|
||||
<el-table :data="nr.bzs" style="width: 100%" border show-summary :summary-method="getSummaries" size="small">
|
||||
<el-table-column align="center" header-align="center" label="序号" type="index" width="70px">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="考评内容" prop="inspection" header-align="center" align="center">
|
||||
<template scope="{row}">
|
||||
<el-input v-if="!is_view" v-model="row.inspection" placeholder="请填写考核标准"
|
||||
maxlength="500" type="textarea" autosize></el-input>
|
||||
<span v-else>{{ row.inspection }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="评分标准" prop="scoringCriteria" header-align="center" align="center" width="300px"
|
||||
min-width="200">
|
||||
<template scope="{row}">
|
||||
<el-input v-if="!is_view" v-model="row.scoringCriteria" placeholder="请填写评分标准"
|
||||
maxlength="250" type="textarea" autosize></el-input>
|
||||
<span v-else>{{ row.scoringCriteria }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="项目自评(分数)" prop="score" header-align="center" align="center" width="120px">
|
||||
<template scope="{row}">
|
||||
<el-input v-if="!is_view" type="number" v-model.number="row.score" maxlength="4"
|
||||
placeholder="请填写项目自评"></el-input>
|
||||
<span v-else>{{ row.score }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="考核办法" prop="assessmentMethod" header-align="center" align="center" width="300px">
|
||||
<template scope="{row}">
|
||||
<el-input v-if="!is_view" v-model="row.assessmentMethod" maxlength="50"
|
||||
placeholder="请填写考核办法"></el-input>
|
||||
<span v-else>{{ row.assessmentMethod }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- <el-table-column label="是否提供佐证材料" prop="isEvidence" header-align="center" align="center"-->
|
||||
<!-- width="120px">-->
|
||||
<!-- <template scope="{row}">-->
|
||||
<!-- <el-switch-->
|
||||
<!-- v-if="!is_view"-->
|
||||
<!-- v-model="row.isEvidence"-->
|
||||
<!-- active-color="#13ce66"-->
|
||||
<!-- inactive-color="#ff4949">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- <el-switch-->
|
||||
<!-- v-else-->
|
||||
<!-- disabled-->
|
||||
<!-- v-model="row.isEvidence">-->
|
||||
<!-- </el-switch>-->
|
||||
<!-- </template>-->
|
||||
<!-- </el-table-column>-->
|
||||
|
||||
|
||||
<el-table-column v-if="!is_view" label="操作" header-align="center" align="center" width="100px">
|
||||
<template slot="header" scope="{row,$index}">
|
||||
<el-button size="mini" icon="el-icon-plus" type="primary" @click="nr.bzs.push({})" title="添加考评内容"></el-button>
|
||||
</template>
|
||||
<template scope="{row,$index}">
|
||||
<el-button size="mini" :disabled="nr.bzs.length<=1 || (!row.canDelete && row.id)" type="danger"
|
||||
icon="el-icon-delete"
|
||||
@click="nr.bzs.splice($index,1)"></el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
<!-- <el-button v-if="!is_view" type="primary" plain style="width: 100%" size="mini" icon="el-icon-plus"-->
|
||||
<!-- @click="nr.bzs.push({})">-->
|
||||
<!-- 添加考核标准-->
|
||||
<!-- </el-button>-->
|
||||
|
||||
<!-- <el-divider></el-divider>-->
|
||||
</el-row>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
const METHOD_NAME = "change"
|
||||
|
||||
module.exports = {
|
||||
props: {
|
||||
is_view: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
default: {
|
||||
nrs: [{
|
||||
bzs: [{}]
|
||||
}],
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
data: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.$emit(METHOD_NAME, val)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isEvaluationIndex: false,
|
||||
zbList: [],
|
||||
formRules: {
|
||||
xxx: [{required: true, message: '请填写年度', trigger: ['blur', 'change']}],
|
||||
},
|
||||
pickerOptions: {
|
||||
disabledDate(time) {
|
||||
return (
|
||||
time.getFullYear() > new Date().getFullYear()
|
||||
);
|
||||
}
|
||||
},
|
||||
relationData: [
|
||||
{moduleName: '提案系统模块', id: 'proposal'},
|
||||
{moduleName: '二级教代会', id: 'secteameet'},
|
||||
{moduleName: '慰问/补助', id: 'ConandDif'},
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getByZbData() {
|
||||
const {code, data, msg} = await $.get('/platform/xhkh/khzb/edit', {id: this.data.zbId})
|
||||
this.$set(this.data, "nrs", data.nrs)
|
||||
},
|
||||
async getZbList() {
|
||||
await $.get("/platform/xhkh/Xjkhzb/getZbList").then(res => {
|
||||
this.zbList = res.data
|
||||
})
|
||||
},
|
||||
async getKh() {
|
||||
await $.get("/platform/xhkh/khph/getKh", {"annual": this.data.annual}).then(res => {
|
||||
if (res.data.length != 0) {
|
||||
this.kh = res.data
|
||||
this.data.khid = this.kh[0].id
|
||||
} else {
|
||||
this.kh = []
|
||||
this.pageForm.khid = ""
|
||||
}
|
||||
})
|
||||
},
|
||||
async khSearch() {
|
||||
await this.getKh()
|
||||
},
|
||||
getSummaries(param) {
|
||||
const {columns, data} = param;
|
||||
const sums = [];
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '小计';
|
||||
return;
|
||||
} else if ([1, 4, 5, 6, 7].includes(index)) {
|
||||
sums[index] = '';
|
||||
return;
|
||||
}
|
||||
const values = data.map(item => Number(item[column.property]));
|
||||
if (!values.every(value => isNaN(value))) {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr);
|
||||
if (!isNaN(value)) {
|
||||
return prev + curr;
|
||||
} else {
|
||||
return prev;
|
||||
}
|
||||
}, 0);
|
||||
sums[index] += ' 分';
|
||||
} else {
|
||||
sums[index] = '';
|
||||
}
|
||||
});
|
||||
return sums;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getZbList()
|
||||
if (!this.data || !Object.keys(this.data).length) {
|
||||
this.data = {
|
||||
nrs: [{
|
||||
bzs: [{}]
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.fixedBox {
|
||||
position: fixed;
|
||||
top: 200px;
|
||||
right: 10px;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.el-divider {
|
||||
background-color: #409EFF;
|
||||
}
|
||||
|
||||
.el_form {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,51 @@
|
||||
/** *Desc: *Create by: jug *Create time:2023/6/8/10:29 */
|
||||
<template>
|
||||
<div>
|
||||
<el-input v-model="cnd.memberSearchKeyWord" placeholder="请输入值" style="width: 100%">
|
||||
<el-select v-model="cnd.memberSearchName" slot="prepend" placeholder="字段" style="width: 80px">
|
||||
<el-option
|
||||
v-for="column in columnInfos"
|
||||
:key="column.COLUMN_NAME"
|
||||
:label="column.COLUMN_COMMENT"
|
||||
:value="column.COLUMN_NAME"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "MemberCnd",
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
columnInfos: [],
|
||||
cnd: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
cnd: {
|
||||
handler(val) {
|
||||
this.$emit("cnd", val)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async getColumnInfo() {
|
||||
const { data, msg, code } = await $.post("/platform/activity/basic/scope/getUserTableColumnInfo")
|
||||
if (code !== 0) {
|
||||
this.$notify.warning(msg)
|
||||
return
|
||||
}
|
||||
this.columnInfos = data
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getColumnInfo()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,195 @@
|
||||
<style>
|
||||
.field .van-field__error-message {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div>
|
||||
<van-form @submit="onSubmit" ref="form">
|
||||
|
||||
<template v-for="column in dynamicData">
|
||||
<!-- 文本、数值、时间 -->
|
||||
<template v-if="['CHAR', 'VARCHAR', 'TEXT', 'INT', 'DATE', 'DATETIME'].includes(column.columnType)">
|
||||
<van-field
|
||||
class="field"
|
||||
v-model="column.columnValue"
|
||||
:clickable="['SELECT'].includes(column.columnFormType)"
|
||||
:label="column.columnName"
|
||||
:name="column.columnCode"
|
||||
:type="['INT'].includes(column.columnType) ? 'digit' : ''"
|
||||
:placeholder="(['SELECT'].includes(column.columnFormType) ? '请选择' : '请填写') + column.columnName"
|
||||
:readonly="['SELECT'].includes(column.columnFormType)"
|
||||
:rules="[{ required: column.isRequired, message: (['SELECT'].includes(column.columnFormType) ? '请选择' : '请填写') + column.columnName }]"
|
||||
:required="column.isRequired"
|
||||
@click="selectFieldClick(column)"
|
||||
>
|
||||
<template v-if="['STEPPER'].includes(column.columnFormType)" #input>
|
||||
<van-stepper v-model="column.columnValue" :max="3" :min="1"/>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<van-popup
|
||||
v-if="['SELECT'].includes(column.columnFormType)"
|
||||
v-model="pickerStates[column.columnCode + 'Picker']"
|
||||
position="bottom"
|
||||
>
|
||||
<template v-if="['DATETIME', 'DATE'].includes(column.columnType)">
|
||||
<van-datetime-picker
|
||||
:title="column.columnName"
|
||||
:type="column.columnType.toLocaleLowerCase()"
|
||||
show-toolbar
|
||||
@cancel="
|
||||
pickerStates[column.columnCode + 'Picker'] = false
|
||||
column.columnValue = ''
|
||||
"
|
||||
@confirm="
|
||||
(time) => {
|
||||
dateTimePickerConfirm(time, column)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<van-picker
|
||||
:columns="column.selectValues"
|
||||
:title="column.columnName"
|
||||
show-toolbar
|
||||
@cancel="
|
||||
pickerStates[column.columnCode + 'Picker'] = false
|
||||
column.columnValue = ''
|
||||
"
|
||||
@confirm="
|
||||
(value) => {
|
||||
ordinaryPickerConfirm(value, column)
|
||||
}
|
||||
"
|
||||
/>
|
||||
</template>
|
||||
</van-popup>
|
||||
</template>
|
||||
<!-- 单选框 -->
|
||||
<template v-else-if="['BOOLEAN'].includes(column.columnType)"></template>
|
||||
<!-- 文件 -->
|
||||
<template v-else-if="['JSON'].includes(column.columnType)">
|
||||
<van-field
|
||||
:required="column.isRequired"
|
||||
:label="column.columnName"
|
||||
:rules="[{ required: column.isRequired, message: '请上传' + column.columnName }]"
|
||||
>
|
||||
<template #input>
|
||||
<!-- <van-uploader v-model="fileList" :after-read="(file)=>{fileAfterRead(file,column)}"
|
||||
:max-count="column.fileNumber"/>-->
|
||||
<h5-file-upload
|
||||
slot="input"
|
||||
:value.sync="column.columnValue"
|
||||
:upload_number="column.fileNumber"
|
||||
upload_mode="file"
|
||||
upload_result_category="array"
|
||||
upload_result_type="url"
|
||||
complete_result
|
||||
></h5-file-upload>
|
||||
</template>
|
||||
</van-field>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</van-form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 请在van-form中引用 -->
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "TrainDynamicForm",
|
||||
components: {},
|
||||
props: {
|
||||
dynamicData: []
|
||||
},
|
||||
model: {
|
||||
prop: "dynamicData",
|
||||
event: "updateDynamicData"
|
||||
},
|
||||
watch: {
|
||||
dynamicData: {
|
||||
handler: function (newValue, oldValue) {
|
||||
this.$emit("updateDynamicData", newValue)
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//所有的picker状态
|
||||
pickerStates: {},
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async validForm() {
|
||||
try {
|
||||
this.$refs.form.validate().then(() => {
|
||||
return true
|
||||
}).catch(() => {
|
||||
return false
|
||||
})
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
},
|
||||
async onSubmit() {
|
||||
|
||||
},
|
||||
selectFieldClick(column) {
|
||||
this.$set(this.pickerStates, column.columnCode + "Picker", true)
|
||||
if (!["DATE", "DATETIME"].includes(column.columnType)) {
|
||||
//说明是普通的下拉框
|
||||
// column.selectValues = ['1', '2', '3']
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 时间picker确认
|
||||
* @param time
|
||||
* @param column
|
||||
*/
|
||||
dateTimePickerConfirm(time, column) {
|
||||
if (column.columnType === "DATE") {
|
||||
column.columnValue = this.$moment(time).format("YYYY-MM-DD")
|
||||
} else if (column.columnType === "DATETIME") {
|
||||
column.columnValue = this.$moment(time).format("YYYY-MM-DD HH:mm:ss")
|
||||
} else {
|
||||
//预留别的类型
|
||||
column.columnValue = this.$moment(time).format("YYYY-MM-DD HH:mm:ss")
|
||||
}
|
||||
this.pickerStates[column.columnCode + "Picker"] = false
|
||||
},
|
||||
/**
|
||||
* 普通picker确认
|
||||
* @param value
|
||||
* @param column
|
||||
*/
|
||||
ordinaryPickerConfirm(value, column) {
|
||||
column.columnValue = value
|
||||
this.pickerStates[column.columnCode + "Picker"] = false
|
||||
},
|
||||
/**
|
||||
* 文件读取
|
||||
*/
|
||||
fileAfterRead(file, column) {
|
||||
console.log(file)
|
||||
console.log(column)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// console.log(this.dynamicData)
|
||||
// this.dynamicData.forEach(v => {
|
||||
// if (v.columnFormType === 'SELECT') {
|
||||
// v.columnPickerName = v.columnCode+'Picker'
|
||||
// }
|
||||
// })
|
||||
// console.log(this.dynamicData)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="process-title">基本信息</div>
|
||||
<el-descriptions :column="3" border class="descriptions-form">
|
||||
<el-descriptions-item label="工号">{{ viewData.loginname }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ viewData.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="姓名">{{ viewData.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="性别">{{ viewData.sex }}</el-descriptions-item>
|
||||
<el-descriptions-item label="出生日期">
|
||||
{{ viewData.birthday ? $moment(viewData.birthday).format("YYYY-MM-DD") : null }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="民族">{{ viewData.nation }}</el-descriptions-item>
|
||||
<el-descriptions-item label="籍贯">{{ viewData.nativePlace }}</el-descriptions-item>
|
||||
<el-descriptions-item label="国籍">{{ viewData.nationality }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件类别">{{ viewData.idCardType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="证件号码">{{ viewData.idCard }}</el-descriptions-item>
|
||||
<el-descriptions-item label="政治面貌">{{ viewData.political }}</el-descriptions-item>
|
||||
<el-descriptions-item label="入党时间">
|
||||
{{ viewData.joinPartyDate ? $moment(viewData.joinPartyDate).format("YYYY-MM-DD") : null }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="来校时间">
|
||||
{{ viewData.arrivalAtSchoolDate ? $moment(viewData.arrivalAtSchoolDate).format("YYYY-MM-DD") : null }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="身份类别">{{ viewData.identityType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="编制类别">{{ viewData.preparedBy }}</el-descriptions-item>
|
||||
<el-descriptions-item label="教职工类别">{{ viewData.personType }}</el-descriptions-item>
|
||||
<el-descriptions-item label="在职状态">{{ viewData.userState }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预计离退休时间">
|
||||
{{ viewData.retireDate ? $moment(viewData.retireDate).format("YYYY-MM-DD") : null }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ viewData.mobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="电子邮箱">{{ viewData.email }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="process-title">会员信息</div>
|
||||
<el-descriptions :column="3" border class="descriptions-form">
|
||||
<el-descriptions-item label="是否会员">
|
||||
<el-tag v-if="viewData.member === 1" size="mini" type="success">是</el-tag>
|
||||
<el-tag v-else type="info" size="mini">否</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所属单位">{{ viewData.unitName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="所属分工会">{{ viewData.unionName }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="process-title">学历信息</div>
|
||||
<el-descriptions :column="3" border class="descriptions-form">
|
||||
<el-descriptions-item label="最高学历">{{ viewData.education }}</el-descriptions-item>
|
||||
<el-descriptions-item label="最高学位">{{ viewData.academicDegree }}</el-descriptions-item>
|
||||
<el-descriptions-item label=""></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="process-title">职称信息</div>
|
||||
<el-descriptions :column="3" border class="descriptions-form">
|
||||
<el-descriptions-item label="职称">{{ viewData.technicalTitle }}</el-descriptions-item>
|
||||
<el-descriptions-item label="职称级别">{{ viewData.technicalTitleLevel }}</el-descriptions-item>
|
||||
<el-descriptions-item label=""></el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div class="process-title">职务信息</div>
|
||||
<el-descriptions :column="3" border class="descriptions-form">
|
||||
<el-descriptions-item label="现聘职务">{{ viewData.position }}</el-descriptions-item>
|
||||
<el-descriptions-item label="现聘职级">{{ viewData.positionLevel }}</el-descriptions-item>
|
||||
<el-descriptions-item label="职员等级">{{ viewData.employeeLevel }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "userInfo",
|
||||
data() {
|
||||
return {
|
||||
viewData: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(id) {
|
||||
if (!id) {
|
||||
return
|
||||
}
|
||||
this.$axios.post("/platform/member/apply/common/findOne", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.viewData = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,252 @@
|
||||
<template>
|
||||
<div class="guava-main-content">
|
||||
<div v-show="v === 'index'" key="index" class="transition-item">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<div v-if="v === 'edit'" key="edit" class="page-container animated-card">
|
||||
<div class="page-header">
|
||||
<div class="header-left">
|
||||
<el-button icon="el-icon-back" style="font-size: 16px; padding: 0" type="text" @click="v = 'index'">返回</el-button>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<slot name="edit_func"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<slot name="edit"></slot>
|
||||
</div>
|
||||
<div class="page-footer" v-if="$slots.edit_footer">
|
||||
<slot name="edit_footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="v === 'view'" key="view" class="page-container animated-card">
|
||||
<div class="page-header">
|
||||
<el-button icon="el-icon-back" style="font-size: 16px; padding: 0" type="text" @click="v = 'index'">返回</el-button>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<slot name="view"></slot>
|
||||
</div>
|
||||
<div class="page-footer" v-if="$slots.view_footer">
|
||||
<slot name="view_footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="v === 'approval'" key="approval" class="page-container animated-card">
|
||||
<div class="page-header">
|
||||
<el-button icon="el-icon-back" style="font-size: 16px; padding: 0" type="text" @click="v = 'index'">返回</el-button>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<slot name="approval"></slot>
|
||||
</div>
|
||||
<div class="page-footer" v-if="$slots.approval_footer">
|
||||
<slot name="approval_footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="v === 'public'" key="public" class="page-container animated-card">
|
||||
<div class="page-header">
|
||||
<el-button icon="el-icon-back" style="font-size: 16px; padding: 0" type="text" @click="back()">返回</el-button>
|
||||
</div>
|
||||
<div class="page-content">
|
||||
<slot name="public"></slot>
|
||||
</div>
|
||||
<div class="page-footer" v-if="$slots.public_footer">
|
||||
<slot name="public_footer"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
module.exports = {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: "index"
|
||||
},
|
||||
//为true的时候页面不会滚动 card-body滚动 edit view 自己来控制
|
||||
edit_scroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
view_scroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
approval_scroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//view 页面是否滚动
|
||||
view_page_scroll: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
public_card_scroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
v: "index"
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(newVal) {
|
||||
this.v = newVal
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
v(newValue, oldValue) {
|
||||
this.$emit("vchange", { newValue, oldValue })
|
||||
this.$emit("input", newValue)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
index(callback = () => {}) {
|
||||
this.v = "index"
|
||||
this.$nextTick(() => {
|
||||
callback()
|
||||
})
|
||||
},
|
||||
edit(callback = () => {}) {
|
||||
this.v = "edit"
|
||||
this.$nextTick(() => {
|
||||
callback()
|
||||
})
|
||||
},
|
||||
view(callback = () => {}) {
|
||||
this.v = "view"
|
||||
this.$nextTick(() => {
|
||||
callback()
|
||||
})
|
||||
},
|
||||
approval(callback = () => {}) {
|
||||
this.v = "approval"
|
||||
this.$nextTick(() => {
|
||||
callback()
|
||||
})
|
||||
},
|
||||
public(callback = () => {}) {
|
||||
this.v = "public"
|
||||
this.$nextTick(() => {
|
||||
callback()
|
||||
})
|
||||
},
|
||||
back(callback = () => {}) {
|
||||
this.v = "index"
|
||||
this.$nextTick(() => {
|
||||
callback()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.guava-main-content {
|
||||
/*height: 100vh;
|
||||
overflow: hidden;*/
|
||||
}
|
||||
|
||||
.page-container {
|
||||
height: calc(100vh - 84px);
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0,0,0,.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
animation-name: cardAnimation;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.page-header {
|
||||
flex-shrink: 0;
|
||||
padding: 12px 24px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-left {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.header-right {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.header-right button{
|
||||
/*padding: 9px 15px;
|
||||
font-size: 12px;*/
|
||||
}
|
||||
|
||||
|
||||
.page-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 20px 24px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 隐藏滚动条但保持滚动功能 */
|
||||
.page-content::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.page-content::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.page-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(0,0,0,0.1);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.page-content::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.page-footer{
|
||||
flex-shrink: 0;
|
||||
padding: 12px 10px;
|
||||
border-top: 1px solid rgb(235, 238, 245);
|
||||
display: flex;
|
||||
justify-content: end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
@keyframes cardAnimation {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.animated-card {
|
||||
animation-name: cardAnimation;
|
||||
animation-duration: 0.3s;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
|
||||
.transition-item {
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,13 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "BpmApproval"
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<slot name="form"></slot>
|
||||
<slot name="process"></slot>
|
||||
<slot name="approval"></slot>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,593 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
group: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
field_options: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
operator_options: {
|
||||
type: Array,
|
||||
default: () => [
|
||||
{ label: "等于", value: "=" },
|
||||
{ label: "不等于", value: "!=" },
|
||||
{ label: "大于", value: ">" },
|
||||
{ label: "小于", value: "<" },
|
||||
{ label: "大于等于", value: ">=" },
|
||||
{ label: "小于等于", value: "<=" },
|
||||
{ label: "包含", value: "LIKE" },
|
||||
{ label: "为空", value: "IS NULL" },
|
||||
{ label: "不为空", value: "IS NOT NULL" },
|
||||
{ label: "在列表中", value: "IN" },
|
||||
{ label: "不在列表中", value: "NOT IN" },
|
||||
{ label: "在范围内", value: "BETWEEN" }
|
||||
]
|
||||
},
|
||||
can_remove: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fieldTypes: {}, // 存储字段类型信息
|
||||
errors: [] // 存储验证错误
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 初始化字段类型信息
|
||||
this.initFieldTypes()
|
||||
},
|
||||
methods: {
|
||||
initFieldTypes() {
|
||||
// 从field_options中提取字段类型信息
|
||||
this.field_options.forEach((field) => {
|
||||
if (field.type) {
|
||||
this.fieldTypes[field.value] = field.type
|
||||
} else {
|
||||
// 默认为字符串类型
|
||||
this.fieldTypes[field.value] = "string"
|
||||
}
|
||||
})
|
||||
},
|
||||
getFieldType(fieldName) {
|
||||
return this.fieldTypes[fieldName] || "string"
|
||||
},
|
||||
addCondition() {
|
||||
if (!this.group.conditions) {
|
||||
this.$set(this.group, "conditions", [])
|
||||
}
|
||||
this.group.conditions.push({
|
||||
field: "",
|
||||
operator: "=",
|
||||
value: "",
|
||||
valid: false
|
||||
})
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
removeCondition(index) {
|
||||
this.group.conditions.splice(index, 1)
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
addGroup() {
|
||||
if (!this.group.groups) {
|
||||
this.$set(this.group, "groups", [])
|
||||
}
|
||||
this.group.groups.push({
|
||||
logic: "AND",
|
||||
conditions: [],
|
||||
groups: []
|
||||
})
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
removeGroup(index) {
|
||||
this.group.groups.splice(index, 1)
|
||||
this.validateAndEmitChange()
|
||||
},
|
||||
isNullOperator(operator) {
|
||||
return operator === "IS NULL" || operator === "IS NOT NULL"
|
||||
},
|
||||
isInOperator(operator) {
|
||||
return operator === "IN" || operator === "NOT IN"
|
||||
},
|
||||
isBetweenOperator(operator) {
|
||||
return operator === "BETWEEN"
|
||||
},
|
||||
validateCondition(condition) {
|
||||
// 检查条件是否有效
|
||||
if (!condition.field || !condition.operator) {
|
||||
return false
|
||||
}
|
||||
|
||||
// 对于IS NULL和IS NOT NULL操作符,不需要值
|
||||
if (this.isNullOperator(condition.operator)) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 对于其他操作符,需要值
|
||||
return condition.value !== undefined && condition.value !== ""
|
||||
},
|
||||
validateGroup(group) {
|
||||
let isValid = true
|
||||
|
||||
// 验证条件
|
||||
if (group.conditions && group.conditions.length > 0) {
|
||||
group.conditions.forEach((condition) => {
|
||||
condition.valid = this.validateCondition(condition)
|
||||
isValid = isValid && condition.valid
|
||||
})
|
||||
}
|
||||
|
||||
// 验证嵌套组
|
||||
if (group.groups && group.groups.length > 0) {
|
||||
group.groups.forEach((nestedGroup) => {
|
||||
isValid = isValid && this.validateGroup(nestedGroup)
|
||||
})
|
||||
}
|
||||
|
||||
// 一个组至少需要一个条件或嵌套组才有效
|
||||
if ((group.conditions && group.conditions.length > 0) || (group.groups && group.groups.length > 0)) {
|
||||
return isValid
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
},
|
||||
formatValue(value, type) {
|
||||
// 根据字段类型格式化值
|
||||
if (value === null || value === undefined || value === "") {
|
||||
return "NULL"
|
||||
}
|
||||
|
||||
switch (type.toLowerCase()) {
|
||||
case "number":
|
||||
case "int":
|
||||
case "integer":
|
||||
case "float":
|
||||
case "double":
|
||||
case "decimal":
|
||||
return value
|
||||
case "date":
|
||||
case "datetime":
|
||||
case "time":
|
||||
case "string":
|
||||
default:
|
||||
// 转义单引号
|
||||
const escaped = String(value).replace(/'/g, "''")
|
||||
return `'${escaped}'`
|
||||
}
|
||||
},
|
||||
buildSql(group = this.group) {
|
||||
let sql = []
|
||||
|
||||
// 处理条件
|
||||
if (group.conditions && group.conditions.length > 0) {
|
||||
group.conditions.forEach((condition) => {
|
||||
if (condition.field && condition.operator) {
|
||||
const fieldType = this.getFieldType(condition.field)
|
||||
|
||||
if (this.isNullOperator(condition.operator)) {
|
||||
sql.push(`${condition.field} ${condition.operator}`)
|
||||
} else if (this.isInOperator(condition.operator) && condition.value) {
|
||||
// 处理IN操作符 - 假设值是以逗号分隔的列表
|
||||
const values = condition.value
|
||||
.split(",")
|
||||
.map((v) => v.trim())
|
||||
.filter((v) => v !== "")
|
||||
.map((v) => this.formatValue(v, fieldType))
|
||||
.join(", ")
|
||||
|
||||
if (values) {
|
||||
sql.push(`${condition.field} ${condition.operator} (${values})`)
|
||||
}
|
||||
} else if (this.isBetweenOperator(condition.operator) && condition.value) {
|
||||
// 处理BETWEEN操作符 - 假设值是以逗号分隔的两个值
|
||||
const parts = condition.value.split(",").map((v) => v.trim())
|
||||
if (parts.length === 2) {
|
||||
const from = this.formatValue(parts[0], fieldType)
|
||||
const to = this.formatValue(parts[1], fieldType)
|
||||
sql.push(`${condition.field} BETWEEN ${from} AND ${to}`)
|
||||
}
|
||||
} else if (condition.value !== undefined && condition.value !== "") {
|
||||
// 其他操作符
|
||||
const formattedValue = this.formatValue(condition.value, fieldType)
|
||||
sql.push(`${condition.field} ${condition.operator} ${formattedValue}`)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 处理嵌套组
|
||||
if (group.groups && group.groups.length > 0) {
|
||||
group.groups.forEach((nestedGroup) => {
|
||||
const nestedSql = this.buildSql(nestedGroup)
|
||||
if (nestedSql) {
|
||||
sql.push(`(${nestedSql})`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 用逻辑运算符连接所有条件
|
||||
return sql.length > 0 ? sql.join(` ${group.logic} `) : ""
|
||||
},
|
||||
validateAndEmitChange() {
|
||||
// 验证所有条件
|
||||
const isValid = this.validateGroup(this.group)
|
||||
|
||||
// 发出变更事件
|
||||
this.$emit("change", {
|
||||
sql: this.buildSql(),
|
||||
valid: isValid
|
||||
})
|
||||
},
|
||||
getOperatorsByFieldType(fieldType) {
|
||||
// 根据字段类型过滤可用的操作符
|
||||
if (!fieldType) return this.operator_options
|
||||
|
||||
const type = fieldType.toLowerCase()
|
||||
return this.operator_options.filter((op) => {
|
||||
// 数值类型
|
||||
if (['number', 'int', 'integer', 'float', 'double', 'decimal'].includes(type)) {
|
||||
// 数值类型不应该使用LIKE操作符
|
||||
return op.value !== 'LIKE'
|
||||
}
|
||||
// 日期类型
|
||||
else if (['date', 'datetime', 'time'].includes(type)) {
|
||||
// 日期类型不应该使用LIKE和IN操作符
|
||||
return !['LIKE', 'IN', 'NOT IN'].includes(op.value)
|
||||
}
|
||||
// 字符串类型,支持所有操作符
|
||||
return true
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
group: {
|
||||
handler: "validateAndEmitChange",
|
||||
deep: true
|
||||
},
|
||||
field_options: {
|
||||
handler: "initFieldTypes",
|
||||
immediate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="condition-builder">
|
||||
<!-- 主逻辑选择和组操作 -->
|
||||
<div class="condition-group-header">
|
||||
<div class="logic-selector">
|
||||
<span class="logic-label">匹配方式:</span>
|
||||
<el-radio-group v-model="group.logic" size="small" @change="validateAndEmitChange">
|
||||
<el-radio-button label="AND">满足所有条件</el-radio-button>
|
||||
<el-radio-button label="OR">满足任意条件</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<el-button v-if="can_remove" type="danger" size="mini" icon="el-icon-delete" @click="$emit('remove')" class="remove-group-btn">
|
||||
删除组
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 条件列表 -->
|
||||
<div class="conditions-container">
|
||||
<div
|
||||
v-for="(condition, index) in group.conditions"
|
||||
:key="'c-' + index"
|
||||
class="condition-row"
|
||||
:class="{ 'invalid-condition': condition.valid === false }"
|
||||
>
|
||||
<div class="condition-index">{{ index + 1 }}</div>
|
||||
|
||||
<el-select
|
||||
v-model="condition.field"
|
||||
filterable
|
||||
placeholder="请选择字段"
|
||||
size="small"
|
||||
class="field-select"
|
||||
@change="validateAndEmitChange"
|
||||
>
|
||||
<el-option v-for="field in field_options" :key="field.value" :label="field.label" :value="field.value">
|
||||
<span>{{ field.label }}</span>
|
||||
<span class="field-type-hint" v-if="field.type">({{ field.type }})</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-select v-model="condition.operator" placeholder="操作符" size="small" class="operator-select" @change="validateAndEmitChange">
|
||||
<el-option
|
||||
v-for="op in getOperatorsByFieldType(getFieldType(condition.field))"
|
||||
:key="op.value"
|
||||
:label="op.label"
|
||||
:value="op.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
|
||||
<!-- 根据操作符类型和字段类型显示不同的输入控件 -->
|
||||
<template v-if="!isNullOperator(condition.operator)">
|
||||
<!-- 对于IN操作符,显示标签输入框 -->
|
||||
<el-input
|
||||
v-if="isInOperator(condition.operator)"
|
||||
v-model="condition.value"
|
||||
placeholder="多个值用逗号分隔"
|
||||
size="small"
|
||||
class="value-input"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
|
||||
<!-- 对于BETWEEN操作符,显示两个输入框 -->
|
||||
<div v-else-if="isBetweenOperator(condition.operator)" class="between-inputs">
|
||||
<el-input
|
||||
v-model="condition.value"
|
||||
placeholder="起始值,结束值"
|
||||
size="small"
|
||||
class="value-input"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
</div>
|
||||
|
||||
<!-- 对于其他操作符,根据字段类型显示不同的输入控件 -->
|
||||
<template v-else>
|
||||
<!-- 数字类型 -->
|
||||
<el-input
|
||||
v-if="['number', 'int', 'integer', 'float', 'double', 'decimal'].includes(getFieldType(condition.field))"
|
||||
v-model.number="condition.value"
|
||||
placeholder="请输入数值"
|
||||
size="small"
|
||||
class="value-input"
|
||||
type="number"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
|
||||
<!-- 日期类型 -->
|
||||
<el-date-picker
|
||||
v-else-if="getFieldType(condition.field) === 'date'"
|
||||
v-model="condition.value"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
size="small"
|
||||
class="value-input"
|
||||
value-format="yyyy-MM-dd"
|
||||
@change="validateAndEmitChange"
|
||||
></el-date-picker>
|
||||
|
||||
<!-- 日期时间类型 -->
|
||||
<el-date-picker
|
||||
v-else-if="getFieldType(condition.field) === 'datetime'"
|
||||
v-model="condition.value"
|
||||
type="datetime"
|
||||
placeholder="选择日期时间"
|
||||
size="small"
|
||||
class="value-input"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"
|
||||
@change="validateAndEmitChange"
|
||||
></el-date-picker>
|
||||
|
||||
<!-- 时间类型 -->
|
||||
<el-time-picker
|
||||
v-else-if="getFieldType(condition.field) === 'time'"
|
||||
v-model="condition.value"
|
||||
placeholder="选择时间"
|
||||
size="small"
|
||||
class="value-input"
|
||||
value-format="HH:mm:ss"
|
||||
@change="validateAndEmitChange"
|
||||
></el-time-picker>
|
||||
|
||||
<!-- 默认为字符串类型 -->
|
||||
<el-input
|
||||
v-else
|
||||
v-model="condition.value"
|
||||
placeholder="请输入值"
|
||||
size="small"
|
||||
class="value-input"
|
||||
@change="validateAndEmitChange"
|
||||
></el-input>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 空值占位,使布局保持一致 -->
|
||||
<div v-else class="value-placeholder"></div>
|
||||
|
||||
<el-button type="danger" size="mini" icon="el-icon-delete" @click="removeCondition(index)" class="remove-btn"></el-button>
|
||||
</div>
|
||||
|
||||
<!-- 空条件提示 -->
|
||||
<div class="empty-condition-hint" v-if="!group.conditions || group.conditions.length === 0">
|
||||
<i class="el-icon-info"></i>
|
||||
请添加筛选条件
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 嵌套条件组 -->
|
||||
<div v-for="(nestedGroup, index) in group.groups" :key="'g-' + index" class="nested-group">
|
||||
<div class="nested-group-header">
|
||||
<span class="nested-group-title">子条件组 {{ index + 1 }}</span>
|
||||
</div>
|
||||
<condition-group
|
||||
:group="nestedGroup"
|
||||
:field_options="field_options"
|
||||
:operator_options="operator_options"
|
||||
:can_remove="true"
|
||||
@remove="removeGroup(index)"
|
||||
@change="validateAndEmitChange"
|
||||
></condition-group>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮区 -->
|
||||
<div class="condition-actions">
|
||||
<el-button type="primary" size="small" icon="el-icon-plus" @click="addCondition">添加条件</el-button>
|
||||
<el-button type="success" size="small" icon="el-icon-folder-add" @click="addGroup">添加条件组</el-button>
|
||||
</div>
|
||||
|
||||
<!-- SQL预览 -->
|
||||
<div class="sql-preview" v-if="buildSql()">
|
||||
<div class="sql-preview-header">
|
||||
<div class="sql-preview-title">条件预览:</div>
|
||||
</div>
|
||||
<el-input type="textarea" :value="buildSql()" readonly :rows="2" class="sql-preview-content"></el-input>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.condition-builder {
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.condition-group-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.logic-label {
|
||||
margin-right: 10px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.conditions-container {
|
||||
padding: 5px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.condition-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
background-color: #f9f9f9;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.condition-row:hover {
|
||||
background-color: #f0f7ff;
|
||||
}
|
||||
|
||||
.invalid-condition {
|
||||
border: 1px dashed #f56c6c;
|
||||
}
|
||||
|
||||
.condition-index {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
text-align: center;
|
||||
background-color: #409eff;
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.field-select {
|
||||
width: 150px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.operator-select {
|
||||
width: 120px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.value-input {
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.between-inputs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.value-placeholder {
|
||||
width: 200px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.field-type-hint {
|
||||
color: #909399;
|
||||
margin-left: 5px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nested-group {
|
||||
margin: 10px 0;
|
||||
padding: 10px 0 10px 20px;
|
||||
border-left: 2px solid #409eff;
|
||||
background-color: #f9fafc;
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.nested-group-header {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.nested-group-title {
|
||||
font-weight: bold;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.condition-actions {
|
||||
margin-top: 15px;
|
||||
padding-top: 15px;
|
||||
border-top: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.remove-btn {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.remove-group-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.sql-preview {
|
||||
margin-top: 20px;
|
||||
padding: 15px;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid #409eff;
|
||||
}
|
||||
|
||||
.sql-preview-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.sql-preview-title {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.sql-preview-content {
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
.empty-condition-hint {
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
color: #909399;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button type="primary" @click="onOpen" size="small">打开设计</el-button>
|
||||
<el-dialog title="表单设计" :visible.sync="dialogVisible" append-to-body width="80%">
|
||||
<fc-designer ref="designerRef" height="100vh" :config="{ showSaveBtn: true }" @save="onSave"></fc-designer>
|
||||
<div slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="onConfirm">确 定</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
value: {
|
||||
type: [Object, String],
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(newVal) {
|
||||
if (newVal && typeof newVal === "object") {
|
||||
this.form = newVal
|
||||
} else if (newVal && typeof newVal === "string") {
|
||||
try {
|
||||
this.form = JSON.parse(newVal)
|
||||
} catch (error) {
|
||||
this.form = {}
|
||||
}
|
||||
}
|
||||
console.log(this.form)
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen() {
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
console.log(this.value)
|
||||
console.log(this.form)
|
||||
console.log(this.$refs.designerRef)
|
||||
if (this.$refs.designerRef && this.form) {
|
||||
this.$refs.designerRef.setOptions(this.form.options)
|
||||
this.$refs.designerRef.setRule(this.form.rule)
|
||||
}
|
||||
})
|
||||
},
|
||||
onSave(val) {
|
||||
console.log(val)
|
||||
this.form = val
|
||||
},
|
||||
onConfirm() {
|
||||
console.log(this.$refs.designerRef)
|
||||
this.$emit("input", this.form)
|
||||
this.dialogVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<div>
|
||||
{{ formKey }}
|
||||
<el-button type="primary" v-for="item in buttons" :key="item.name" @click="buttonClick(item)" size="small">
|
||||
{{ item.name }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
taskId: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
formKey: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
buttons: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getButtons() {
|
||||
$.get("/platform/flow/common/loadSequenceFlows", { taskId: this.taskId }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.buttons = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
buttonClick(item) {
|
||||
this.$root.$refs[this.formKey].validate((valid) => {
|
||||
if (valid) {
|
||||
this.$confirm(`您确定要${item.name}吗,${item.name}后将流转到${item.toNode},是否继续?`, "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
})
|
||||
.then(() => {
|
||||
this.$emit("confirm", item.name)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
taskId: {
|
||||
handler: function (val) {
|
||||
if (val) {
|
||||
this.getButtons()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,242 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- :is="mode === 'dialog' ? 'el-dialog' : 'div'"-->
|
||||
<!-- title="详情" v-bind="$attrs"-->
|
||||
<div v-if="visible">
|
||||
<el-tabs v-model="tabActive">
|
||||
<el-tab-pane name="info" label="基本信息">
|
||||
<slot name="businessInfo"></slot>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="processRecords" label="审批记录">
|
||||
<slot name="processRecords"></slot>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane name="approvalChart" label="流程跟踪">
|
||||
<snaker-flow-designer
|
||||
style="height: 600px"
|
||||
v-model="flowData"
|
||||
v-if="visible"
|
||||
:show-doc="false"
|
||||
:viewer="true"
|
||||
nodeRenderType="html"
|
||||
:wfConfig="{
|
||||
showHelp: false
|
||||
}"
|
||||
:high-light="highLight"
|
||||
></snaker-flow-designer>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-row
|
||||
class="audit-wrap"
|
||||
v-if="visible && isAudit && record && record.processTaskState === $processConstant.TASK_STATE.DOING && tabActive !== 'approvalChart'"
|
||||
>
|
||||
<div class="left-span-label" style="margin-top: 20px; margin-bottom: 20px">
|
||||
{{ record.processTaskDisplayName }}
|
||||
</div>
|
||||
<slot name="form"></slot>
|
||||
<el-row type="flex" justify="end" class="flow-button-wrap">
|
||||
<el-button
|
||||
@click="
|
||||
visible = false
|
||||
$emit('close')
|
||||
"
|
||||
size="small"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
<button
|
||||
v-for="item in buttons"
|
||||
:key="item.type"
|
||||
class="task-button"
|
||||
:style="buttonStyle(item)"
|
||||
@click="buttonClick(item)"
|
||||
@mouseenter="buttonMouseEnter($event, item)"
|
||||
@mouseleave="buttonMouseLeave($event, item)"
|
||||
>
|
||||
{{ item.replaceText || item.text }}
|
||||
</button>
|
||||
</el-row>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "flowAuditIndex",
|
||||
props: {
|
||||
form_valid_success: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
//监听父组件的表单是否验证成功
|
||||
form_valid_success(val) {
|
||||
if (val) {
|
||||
this.buttonClick(this.currentClickButton)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
tabActive: "info",
|
||||
//是否需要审核
|
||||
isAudit: false,
|
||||
// 回退列表数据
|
||||
returnTaskList: [],
|
||||
//回退弹窗
|
||||
returnOpen: false,
|
||||
//任务记录
|
||||
record: {},
|
||||
//审核按钮列表
|
||||
buttons: [],
|
||||
//任务表单
|
||||
taskForm: {},
|
||||
//当前点击的按钮
|
||||
currentClickButton: {},
|
||||
//流程数据
|
||||
flowData: {},
|
||||
//高亮数据
|
||||
highLight: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buttonStyle(item) {
|
||||
return {
|
||||
background: item.color
|
||||
}
|
||||
},
|
||||
buttonMouseEnter($event, item) {
|
||||
console.log($event)
|
||||
console.log(item)
|
||||
},
|
||||
buttonMouseLeave() {},
|
||||
|
||||
onOpen(record, isAudit = false) {
|
||||
this.isAudit = isAudit
|
||||
this.record = record
|
||||
this.getApprovalChart(record.processInstId)
|
||||
if (isAudit) {
|
||||
this.getButtons()
|
||||
}
|
||||
},
|
||||
//关闭
|
||||
onClose() {
|
||||
this.visible = false
|
||||
},
|
||||
//流程图数据
|
||||
getApprovalChart(id) {
|
||||
$.get("/platform/wf/processCommon/processJsonHighLight", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.flowData = res.data.instJson
|
||||
this.highLight = res.data.highLight
|
||||
this.visible = true
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getButtons() {
|
||||
const { processTaskName, processDefineId } = this.record
|
||||
$.get("/platform/wf/processCommon/listButtonByTaskName", {
|
||||
defineId: processDefineId,
|
||||
taskName: processTaskName
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.buttons = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
buttonClick(item) {
|
||||
this.currentClickButton = item
|
||||
this.$emit("form_valid", item.type)
|
||||
if (!this.form_valid_success) return
|
||||
|
||||
//指定下一节点审批人 只有在同意的情况下
|
||||
// if (!["AGREE"].includes(item.type)) {
|
||||
// }
|
||||
|
||||
// if (item.type === "JUMP") {
|
||||
// return
|
||||
// }
|
||||
if (this.form_valid_success) {
|
||||
this.submitTask()
|
||||
}
|
||||
},
|
||||
|
||||
submitReturn() {
|
||||
this.$refs.taskFormRef.validate((valid) => {
|
||||
if (valid) {
|
||||
this.returnOpen = false
|
||||
this.submitTask()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
submitTask() {
|
||||
this.$confirm("您确定要提交吗?", "提示", {
|
||||
confirmButtonText: "确定",
|
||||
cancelButtonText: "取消",
|
||||
type: "info"
|
||||
})
|
||||
.then(() => {
|
||||
this.taskForm.processTaskId = this.record.processTaskId
|
||||
this.taskForm.processInstId = this.record.processInstId
|
||||
this.taskForm.submitType = this.currentClickButton.code
|
||||
this.$emit("form_confirm", this.taskForm)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.audit-wrap {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid var(--border-color-lighter);
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.flow-button-wrap {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid var(--border-color-lighter);
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.el-button + .task-button,
|
||||
.task-button + .task-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.task-button {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
/*background: var(--button-default-background-color);*/
|
||||
/*border: 1px solid var(--border-color-base);*/
|
||||
border: none;
|
||||
color: var(--color-white);
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
-webkit-transition: 0.1s;
|
||||
transition: 0.1s;
|
||||
font-weight: 500;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
padding: 9px 15px;
|
||||
font-size: 12px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="visible">
|
||||
<van-tabs v-model="tabActive">
|
||||
<van-tab name="info" title="基本信息">
|
||||
<slot name="businessInfo"></slot>
|
||||
</van-tab>
|
||||
<van-tab name="processRecords" title="审批记录">
|
||||
<slot name="processRecords"></slot>
|
||||
</van-tab>
|
||||
<van-tab name="approvalChart" title="流程跟踪">
|
||||
<snaker-flow-designer
|
||||
style="height: 600px"
|
||||
v-model="flowData"
|
||||
v-if="visible"
|
||||
:show-doc="false"
|
||||
:viewer="true"
|
||||
nodeRenderType="html"
|
||||
:wfConfig="{
|
||||
showHelp: false
|
||||
}"
|
||||
:high-light="highLight"
|
||||
></snaker-flow-designer>
|
||||
</van-tab>
|
||||
</van-tabs>
|
||||
<van-row
|
||||
class="audit-wrap"
|
||||
v-if="visible && isAudit && record && record.processTaskState === $processConstant.TASK_STATE.DOING && tabActive !== 'approvalChart'"
|
||||
>
|
||||
<div class="left-span-label" style="margin-top: 10px; margin-bottom: 10px">
|
||||
<van-text-box type="4" style="padding: 0 10px 0" :title="record.processTaskDisplayName"></van-text-box>
|
||||
</div>
|
||||
<slot name="form"></slot>
|
||||
<van-row type="flex" justify="end" class="flow-button-wrap">
|
||||
<van-button
|
||||
style="margin-right: 5px"
|
||||
@click="
|
||||
visible = false
|
||||
$emit('close')
|
||||
"
|
||||
size="middle"
|
||||
>
|
||||
取消
|
||||
</van-button>
|
||||
<button
|
||||
v-for="item in buttons"
|
||||
:key="item.type"
|
||||
class="task-button"
|
||||
:style="buttonStyle(item)"
|
||||
@click="buttonClick(item)"
|
||||
@mouseenter="buttonMouseEnter($event, item)"
|
||||
@mouseleave="buttonMouseLeave($event, item)"
|
||||
>
|
||||
{{ item.replaceText || item.text }}
|
||||
</button>
|
||||
</van-row>
|
||||
</van-row>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "flowH5AuditIndex",
|
||||
props: {
|
||||
form_valid_success: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
//监听父组件的表单是否验证成功
|
||||
form_valid_success(val) {
|
||||
if (val) {
|
||||
this.buttonClick(this.currentClickButton)
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
tabActive: "info",
|
||||
//是否需要审核
|
||||
isAudit: false,
|
||||
// 回退列表数据
|
||||
returnTaskList: [],
|
||||
//回退弹窗
|
||||
returnOpen: false,
|
||||
//任务记录
|
||||
record: {},
|
||||
//审核按钮列表
|
||||
buttons: [],
|
||||
//任务表单
|
||||
taskForm: {},
|
||||
//当前点击的按钮
|
||||
currentClickButton: {},
|
||||
//流程数据
|
||||
flowData: {},
|
||||
//高亮数据
|
||||
highLight: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buttonStyle(item) {
|
||||
return {
|
||||
background: item.color
|
||||
}
|
||||
},
|
||||
buttonMouseEnter($event, item) {
|
||||
console.log($event)
|
||||
console.log(item)
|
||||
},
|
||||
buttonMouseLeave() {},
|
||||
|
||||
onOpen(record, isAudit = false) {
|
||||
this.isAudit = isAudit
|
||||
this.record = record
|
||||
this.getApprovalChart(record.processInstId)
|
||||
if (isAudit) {
|
||||
this.getButtons()
|
||||
}
|
||||
},
|
||||
//关闭
|
||||
onClose() {
|
||||
this.visible = false
|
||||
},
|
||||
//流程图数据
|
||||
getApprovalChart(id) {
|
||||
$.get("/platform/wf/processCommon/processJsonHighLight", { id }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.flowData = res.data.instJson
|
||||
this.highLight = res.data.highLight
|
||||
this.visible = true
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
getButtons() {
|
||||
const { processTaskName, processDefineId } = this.record
|
||||
$.get("/platform/wf/processCommon/listButtonByTaskName", {
|
||||
defineId: processDefineId,
|
||||
taskName: processTaskName
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.buttons = res.data
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
buttonClick(item) {
|
||||
this.currentClickButton = item
|
||||
this.$emit("form_valid", item.type)
|
||||
if (!this.form_valid_success) return
|
||||
|
||||
//指定下一节点审批人 只有在同意的情况下
|
||||
// if (!["AGREE"].includes(item.type)) {
|
||||
// }
|
||||
|
||||
// if (item.type === "JUMP") {
|
||||
// return
|
||||
// }
|
||||
if (this.form_valid_success) {
|
||||
this.submitTask()
|
||||
}
|
||||
},
|
||||
|
||||
submitReturn() {
|
||||
this.$refs.taskFormRef.validate((valid) => {
|
||||
if (valid) {
|
||||
this.returnOpen = false
|
||||
this.submitTask()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
submitTask() {
|
||||
this.$dialog
|
||||
.confirm({
|
||||
title: "提示",
|
||||
message: "您确定要提交吗?"
|
||||
})
|
||||
.then(() => {
|
||||
this.taskForm.processTaskId = this.record.processTaskId
|
||||
this.taskForm.processInstId = this.record.processInstId
|
||||
this.taskForm.submitType = this.currentClickButton.code
|
||||
this.$emit("form_confirm", this.taskForm)
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.audit-wrap {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid var(--border-color-lighter);
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.flow-button-wrap {
|
||||
margin-top: 20px;
|
||||
border-top: 1px solid var(--border-color-lighter);
|
||||
padding-top: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.el-tabs__content {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.el-button + .task-button,
|
||||
.task-button + .task-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.task-button {
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
/*background: var(--button-default-background-color);*/
|
||||
/*border: 1px solid var(--border-color-base);*/
|
||||
border: none;
|
||||
color: var(--color-white);
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
margin: 0;
|
||||
-webkit-transition: 0.1s;
|
||||
transition: 0.1s;
|
||||
font-weight: 500;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
padding: 9px 15px;
|
||||
font-size: 12px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,57 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-card v-if="!visible">
|
||||
<div></div>
|
||||
2222222222222222222222222222222222222222 2222222222222222222222222222222222222222 2222222222222222222222222222222222222222
|
||||
2222222222222222222222222222222222222222 申请需要的说明 流程图等等等等等等等等等等
|
||||
|
||||
<el-row :gutter="10">
|
||||
<el-button type="primary" @click="onStart">开始申请</el-button>
|
||||
</el-row>
|
||||
</el-card>
|
||||
<template v-if="visible">
|
||||
<slot></slot>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
taskId: parseInt(GetQueryString("TASK_ID"))
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onStart() {
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "Loading",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)"
|
||||
})
|
||||
|
||||
$.post(loc() + "/startProcess").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$emit("task_id", res.data.taskId)
|
||||
this.$emit("business_id", res.data.businessId)
|
||||
setTimeout(() => {
|
||||
loading.close()
|
||||
this.visible = true
|
||||
}, 1000)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.taskId) {
|
||||
this.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,648 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "formButton",
|
||||
props: {
|
||||
task_id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 是否显示审核意见框
|
||||
show_comment: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 自定义按钮配置
|
||||
custom_buttons: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
// 是否显示流程图
|
||||
show_process_diagram: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 是否显示办理记录
|
||||
show_process_records: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 办理记录展示模式:timeline, table
|
||||
record_display_mode: {
|
||||
type: String,
|
||||
default: "timeline",
|
||||
validator: (value) => {
|
||||
return ["timeline", "table"].includes(value)
|
||||
}
|
||||
},
|
||||
// 业务ID,用于获取流程记录
|
||||
business_id: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
comment: "", // 审核意见
|
||||
nextNodeInfo: null, // 下一节点信息
|
||||
currentTaskName: "部门审核", // 当前办理节点名称
|
||||
availableActions: [], // 可用操作
|
||||
sequenceFlows: [], // 目标节点
|
||||
selectedAction: "", // 选中的操作类型
|
||||
rejectTaskOptions: [], // 驳回到指定节点的选项
|
||||
selectedRejectTask: "", // 选中的驳回节点
|
||||
assigneeOptions: [], // 执行人选项
|
||||
selectedAssignee: "", // 选中的执行人
|
||||
|
||||
// 控制显示
|
||||
showProcessDiagram: true, // 是否显示流程图
|
||||
showProcessRecords: true, // 是否显示办理记录
|
||||
recordDisplayMode: 'timeline', // 办理记录展示模式:timeline, table
|
||||
|
||||
// 模拟数据 - 办理记录
|
||||
mockProcessRecords: [
|
||||
{
|
||||
nodeName: "发起申请",
|
||||
userName: "张三",
|
||||
time: "2023-05-10 09:30:00",
|
||||
action: "提交申请",
|
||||
comment: "请审批",
|
||||
type: "primary"
|
||||
},
|
||||
{
|
||||
nodeName: "部门审核",
|
||||
userName: "李四",
|
||||
time: "2023-05-10 11:20:00",
|
||||
action: "处理中",
|
||||
comment: "",
|
||||
type: "warning"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.task_id) {
|
||||
this.getNextNodeInfo()
|
||||
}
|
||||
|
||||
// 初始化显示状态
|
||||
this.showProcessDiagram = this.show_process_diagram
|
||||
this.showProcessRecords = this.show_process_records
|
||||
this.recordDisplayMode = this.record_display_mode
|
||||
|
||||
// 模拟获取流程记录
|
||||
this.mockGetProcessRecords()
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 监听props变化
|
||||
show_process_diagram(val) {
|
||||
this.showProcessDiagram = val
|
||||
},
|
||||
show_process_records(val) {
|
||||
this.showProcessRecords = val
|
||||
},
|
||||
record_display_mode(val) {
|
||||
this.recordDisplayMode = val
|
||||
},
|
||||
business_id: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
// 清空现有记录
|
||||
this.mockProcessRecords = []
|
||||
// 重新获取流程记录
|
||||
this.mockGetProcessRecords()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取下一节点信息
|
||||
getNextNodeInfo() {
|
||||
this.loading = true
|
||||
this.$axios
|
||||
.post("/easy-flowable/task/nextNodeVariables/" + this.task_id)
|
||||
.then((res) => {
|
||||
if (res.code === 200) {
|
||||
this.nextNodeInfo = res.result
|
||||
// 获取当前办理节点名称
|
||||
if (res.result.task && res.result.task.name) {
|
||||
this.currentTaskName = res.result.task.name
|
||||
}
|
||||
// 处理可用操作
|
||||
if (res.result.attributes && res.result.attributes.actions) {
|
||||
this.availableActions = res.result.attributes.actions
|
||||
}
|
||||
// 处理目标节点
|
||||
if (res.result.sequenceFlow) {
|
||||
this.sequenceFlows = res.result.sequenceFlow
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.msg || "获取下一节点信息失败")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("获取下一节点信息失败", error)
|
||||
this.$message.error("获取下一节点信息失败")
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取可驳回的节点列表
|
||||
getRejectTaskList() {
|
||||
this.loading = true
|
||||
this.$axios
|
||||
.get("/easy-flowable/task/flowBackNodes/" + this.task_id)
|
||||
.then((res) => {
|
||||
if (res.code === 0 && res.result) {
|
||||
this.rejectTaskOptions = res.result.map((item) => ({
|
||||
label: item.nodeName,
|
||||
value: item.nodeId
|
||||
}))
|
||||
if (this.rejectTaskOptions.length > 0) {
|
||||
this.selectedRejectTask = this.rejectTaskOptions[0].value
|
||||
}
|
||||
} else {
|
||||
this.$message.error(res.msg || "获取可驳回节点失败")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("获取可驳回节点失败", error)
|
||||
this.$message.error("获取可驳回节点失败")
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 处理按钮点击
|
||||
handleAction(action) {
|
||||
this.selectedAction = action
|
||||
|
||||
// 如果是驳回到指定节点,需要获取可驳回的节点列表
|
||||
if (action === "REJECT_TO_TASK") {
|
||||
this.getRejectTaskList()
|
||||
return
|
||||
}
|
||||
|
||||
// 执行操作
|
||||
this.submitAction()
|
||||
},
|
||||
// 提交操作
|
||||
submitAction() {
|
||||
if (this.show_comment && !this.comment && ["AGREE", "REJECT", "REJECT_TO_TASK", "REBUT"].includes(this.selectedAction)) {
|
||||
this.$message.warning("请填写审核意见")
|
||||
return
|
||||
}
|
||||
|
||||
const params = {
|
||||
taskId: this.task_id,
|
||||
comment: this.comment,
|
||||
type: this.getActionCode(this.selectedAction)
|
||||
}
|
||||
|
||||
// 添加目标节点变量(自动选择第一个)
|
||||
if (this.sequenceFlows.length > 0 && ["AGREE"].includes(this.selectedAction)) {
|
||||
params.variables = {
|
||||
sequenceFlow: this.sequenceFlows[0].value
|
||||
}
|
||||
}
|
||||
|
||||
// 添加驳回节点信息
|
||||
if (this.selectedAction === "REJECT_TO_TASK" && this.selectedRejectTask) {
|
||||
params.targetTaskDefinitionKey = this.selectedRejectTask
|
||||
}
|
||||
|
||||
this.loading = true
|
||||
this.$axios
|
||||
.post("/easy-flowable/task/complete", params)
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("操作成功")
|
||||
this.$emit("success", {
|
||||
action: this.selectedAction,
|
||||
result: res.result
|
||||
})
|
||||
} else {
|
||||
this.$message.error(res.msg || "操作失败")
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("操作失败", error)
|
||||
this.$message.error("操作失败")
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 获取操作类型代码
|
||||
getActionCode(action) {
|
||||
const actionMap = {
|
||||
START: "0",
|
||||
AGREE: "1",
|
||||
REBUT: "2",
|
||||
REVOCATION: "3",
|
||||
REJECT: "4",
|
||||
REJECT_TO_TASK: "5",
|
||||
DELEGATE: "6",
|
||||
ASSIGN: "7",
|
||||
STOP: "8",
|
||||
BEFORE_SIGN: "9",
|
||||
AFTER_SIGN: "10",
|
||||
CANCELLATION: "11",
|
||||
ADD_COMMENT: "12",
|
||||
DEL_COMMENT: "13",
|
||||
RESUBMIT: "14"
|
||||
}
|
||||
return actionMap[action] || "1"
|
||||
},
|
||||
// 获取操作按钮文本
|
||||
getActionText(action) {
|
||||
const actionTextMap = {
|
||||
START: "启动流程",
|
||||
AGREE: "同意",
|
||||
REBUT: "拒绝",
|
||||
REVOCATION: "撤回",
|
||||
REJECT: "驳回",
|
||||
REJECT_TO_TASK: "驳回到指定节点",
|
||||
DELEGATE: "委派",
|
||||
ASSIGN: "转办",
|
||||
STOP: "终止",
|
||||
BEFORE_SIGN: "前加签",
|
||||
AFTER_SIGN: "后加签",
|
||||
CANCELLATION: "作废",
|
||||
ADD_COMMENT: "添加评论",
|
||||
DEL_COMMENT: "删除评论",
|
||||
RESUBMIT: "重新提交"
|
||||
}
|
||||
return actionTextMap[action] || action
|
||||
},
|
||||
// 获取操作按钮类型
|
||||
getActionType(action) {
|
||||
const typeMap = {
|
||||
AGREE: "primary",
|
||||
REBUT: "danger",
|
||||
REJECT: "warning",
|
||||
REJECT_TO_TASK: "warning"
|
||||
}
|
||||
return typeMap[action] || "default"
|
||||
},
|
||||
|
||||
// 模拟获取流程记录
|
||||
mockGetProcessRecords() {
|
||||
// 这里可以根据实际需求调整模拟数据
|
||||
// 实际项目中,可以通过API获取真实数据
|
||||
// 例如:this.$axios.post("/platform/wf/processCommon/approvalRecord", { businessNo: this.business_id })
|
||||
|
||||
// 模拟异步获取数据
|
||||
setTimeout(() => {
|
||||
// 如果已经有模拟数据,则不再添加
|
||||
if (this.mockProcessRecords.length <= 2) {
|
||||
// 添加更多模拟数据
|
||||
this.mockProcessRecords.push(
|
||||
{
|
||||
nodeName: "主管审核",
|
||||
userName: "王五",
|
||||
time: "2023-05-09 14:30:00",
|
||||
action: "同意",
|
||||
comment: "同意申请,请总经理审批",
|
||||
type: "success"
|
||||
},
|
||||
{
|
||||
nodeName: "发起申请",
|
||||
userName: "张三",
|
||||
time: "2023-05-09 10:15:00",
|
||||
action: "提交申请",
|
||||
comment: "请审批我的申请",
|
||||
type: "info"
|
||||
}
|
||||
)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="form-button-container">
|
||||
<!-- 主要内容区域 -->
|
||||
<div class="main-content">
|
||||
<!-- 表单信息插槽 -->
|
||||
<div class="form-info-container">
|
||||
<slot name="form-info"></slot>
|
||||
</div>
|
||||
|
||||
<!-- 流程图展示 -->
|
||||
<div class="process-diagram-container" v-if="showProcessDiagram">
|
||||
<div class="section-title">流程图</div>
|
||||
<div class="process-diagram">
|
||||
<!-- 模拟流程图 -->
|
||||
<div class="mock-process-diagram">
|
||||
<div class="mock-node start">开始</div>
|
||||
<div class="mock-arrow"></div>
|
||||
<div class="mock-node" :class="{ active: currentTaskName === '部门审核' }">部门审核</div>
|
||||
<div class="mock-arrow"></div>
|
||||
<div class="mock-node">主管审核</div>
|
||||
<div class="mock-arrow"></div>
|
||||
<div class="mock-node">总经理审核</div>
|
||||
<div class="mock-arrow"></div>
|
||||
<div class="mock-node end">结束</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 办理记录 -->
|
||||
<div class="process-records-container" v-if="showProcessRecords">
|
||||
<div class="section-title">
|
||||
<span>办理记录</span>
|
||||
<div class="display-mode-selector">
|
||||
<el-radio-group v-model="recordDisplayMode" size="small">
|
||||
<el-radio-button label="timeline">时间线</el-radio-button>
|
||||
<el-radio-button label="table">表格</el-radio-button>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="process-records">
|
||||
<!-- 时间线模式 -->
|
||||
<el-timeline v-if="recordDisplayMode === 'timeline'">
|
||||
<el-timeline-item v-for="(record, index) in mockProcessRecords" :key="index" :timestamp="record.time" :type="record.type">
|
||||
<div class="record-content">
|
||||
<div class="record-title">{{ record.nodeName }} - {{ record.userName }}</div>
|
||||
<div class="record-action">{{ record.action }}</div>
|
||||
<div class="record-comment" v-if="record.comment">{{ record.comment }}</div>
|
||||
</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
|
||||
<!-- 表格模式 -->
|
||||
<el-table v-else-if="recordDisplayMode === 'table'" :data="mockProcessRecords" stripe style="width: 100%">
|
||||
<el-table-column prop="nodeName" label="节点名称" width="120"></el-table-column>
|
||||
<el-table-column prop="userName" label="操作人" width="100"></el-table-column>
|
||||
<el-table-column prop="time" label="操作时间" width="160"></el-table-column>
|
||||
<el-table-column prop="action" label="操作类型" width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-tag :type="scope.row.type">{{ scope.row.action }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="comment" label="操作意见">
|
||||
<template slot-scope="scope">
|
||||
<span v-if="scope.row.comment">{{ scope.row.comment }}</span>
|
||||
<span v-else class="no-comment">无</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部固定操作区域 -->
|
||||
<div class="footer-actions">
|
||||
<!-- 当前办理节点 -->
|
||||
<div v-if="currentTaskName" class="current-task-container">
|
||||
<div class="current-task-label">当前节点:</div>
|
||||
<div class="current-task-info">{{ currentTaskName }}</div>
|
||||
</div>
|
||||
|
||||
<!-- 目标节点展示 -->
|
||||
<div v-if="sequenceFlows.length > 0" class="flow-container">
|
||||
<div class="flow-label">目标节点:</div>
|
||||
<div class="target-node-info">
|
||||
<span v-for="item in sequenceFlows" :key="item.value">{{ item.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 审核意见 -->
|
||||
<div v-if="show_comment" class="comment-container">
|
||||
<div class="comment-label">审核意见:</div>
|
||||
<el-input v-model="comment" type="textarea" :rows="3" placeholder="请输入审核意见"></el-input>
|
||||
</div>
|
||||
|
||||
<!-- 驳回到指定节点选择 -->
|
||||
<div v-if="selectedAction === 'REJECT_TO_TASK' && rejectTaskOptions.length > 0" class="reject-container">
|
||||
<div class="reject-label">驳回到:</div>
|
||||
<el-select v-model="selectedRejectTask" placeholder="请选择驳回节点">
|
||||
<el-option v-for="item in rejectTaskOptions" :key="item.value" :label="item.label" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
<el-button type="primary" @click="submitAction">确认</el-button>
|
||||
<el-button @click="selectedAction = ''">取消</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 操作按钮 -->
|
||||
<div class="button-container">
|
||||
<!-- 系统预设按钮 -->
|
||||
<template v-for="action in availableActions">
|
||||
<el-button
|
||||
:key="action"
|
||||
:type="getActionType(action)"
|
||||
:loading="loading && selectedAction === action"
|
||||
@click="handleAction(action)"
|
||||
>
|
||||
{{ getActionText(action) }}
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<!-- 自定义按钮 -->
|
||||
<template v-for="(button, index) in custom_buttons">
|
||||
<el-button
|
||||
:key="'custom-' + index"
|
||||
:type="button.type || 'default'"
|
||||
:loading="loading && selectedAction === button.action"
|
||||
@click="handleAction(button.action)"
|
||||
>
|
||||
{{ button.text }}
|
||||
</el-button>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.form-button-container {
|
||||
/*display: flex;
|
||||
//flex-direction: column;*/
|
||||
min-height: 500px;
|
||||
position: relative;
|
||||
height: 100vh; /* 使用视口高度 */
|
||||
}
|
||||
|
||||
/* 主要内容区域 */
|
||||
.main-content {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 表单信息容器 */
|
||||
.form-info-container {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 流程图和办理记录共用样式 */
|
||||
.process-diagram-container,
|
||||
.process-records-container {
|
||||
margin-bottom: 30px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.display-mode-selector {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
/* 流程图样式 */
|
||||
.process-diagram {
|
||||
min-height: 120px;
|
||||
}
|
||||
|
||||
.mock-process-diagram {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.mock-node {
|
||||
padding: 10px 15px;
|
||||
background-color: #f5f7fa;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
margin: 0 5px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mock-node.start {
|
||||
background-color: #f0f9eb;
|
||||
border-color: #e1f3d8;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.mock-node.end {
|
||||
background-color: #f0f2f5;
|
||||
border-color: #dcdfe6;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.mock-node.active {
|
||||
background-color: #ecf5ff;
|
||||
border-color: #d9ecff;
|
||||
color: #409eff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.mock-arrow {
|
||||
width: 30px;
|
||||
height: 2px;
|
||||
background-color: #dcdfe6;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mock-arrow:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 4px 0 4px 6px;
|
||||
border-color: transparent transparent transparent #dcdfe6;
|
||||
}
|
||||
|
||||
/* 办理记录样式 */
|
||||
.process-records {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* 时间线模式样式 */
|
||||
.record-content {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.record-title {
|
||||
font-weight: bold;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.record-action {
|
||||
color: #409eff;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.record-comment {
|
||||
color: #606266;
|
||||
background-color: #f5f7fa;
|
||||
padding: 8px;
|
||||
border-radius: 4px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* 表格模式样式 */
|
||||
.no-comment {
|
||||
color: #909399;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* 底部固定操作区域 */
|
||||
.footer-actions {
|
||||
border-top: 1px solid #ebeef5;
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
//position: sticky;
|
||||
//bottom: 0;
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.comment-container,
|
||||
.current-task-container,
|
||||
.flow-container,
|
||||
.reject-container {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.comment-label,
|
||||
.current-task-label,
|
||||
.flow-label,
|
||||
.reject-label {
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.current-task-info {
|
||||
padding: 5px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.target-node-info {
|
||||
padding: 5px 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.button-container {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "TableColumn",
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
value: {
|
||||
type: [String, Number],
|
||||
required: false
|
||||
},
|
||||
label_suffix:{
|
||||
type: String,
|
||||
default: ":"
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="table-column">
|
||||
<!-- 左侧 Label -->
|
||||
<div class="label">
|
||||
<slot name="label">
|
||||
{{ label }}
|
||||
{{label_suffix}}
|
||||
</slot>
|
||||
</div>
|
||||
<!-- 右侧 Value -->
|
||||
<div class="value">
|
||||
<slot>{{ value }}</slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.table-column {
|
||||
display: flex;
|
||||
padding: 6px 0;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #333;
|
||||
font-size: 14px;
|
||||
flex-shrink: 0;
|
||||
max-width: 120px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #555;
|
||||
font-size: 14px;
|
||||
flex-grow: 1; /* 动态占据剩余部分 */
|
||||
white-space: nowrap; /* 防止换行 */
|
||||
overflow: hidden; /* 隐藏溢出的部分 */
|
||||
text-overflow: ellipsis; /* 省略号 */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,293 @@
|
||||
<template>
|
||||
<van-pull-refresh v-model="tableRefreshing" @refresh="onRefresh">
|
||||
<div v-if="tableData.length === 0" class="empty-state">
|
||||
<van-empty description="暂无数据"></van-empty>
|
||||
</div>
|
||||
<van-list v-if="tableData && tableData.length>0"
|
||||
v-model="tableLoading"
|
||||
:finished="tableFinished"
|
||||
finished-text="没有更多了"
|
||||
@load="onLoad">
|
||||
<div class="table-list-container">
|
||||
<div v-for="(row, index) in tableData" :key="index" class="table-list-item">
|
||||
<slot name="header" :index="index" :row="row">
|
||||
<div class="item-header">
|
||||
<div class="item-title">{{ calcTitle(row) }}</div>
|
||||
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
|
||||
size="small"></enum-tag>
|
||||
</div>
|
||||
</slot>
|
||||
|
||||
<div style="display: flex;column-gap: 10px"
|
||||
:style="{'max-height':img ? '100px':'unset', 'overflow': img ? 'hidden':'unset' }">
|
||||
<div class="img-container" v-if="img">
|
||||
<img :src="row[img]" alt="" style="object-fit: cover">
|
||||
</div>
|
||||
<div style="width: 100%">
|
||||
<slot :index="index" :row="row"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-actions">
|
||||
<slot name="actions" :index="index" :row="row"></slot>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</van-list>
|
||||
</van-pull-refresh>
|
||||
</template>
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "TableList",
|
||||
props: {
|
||||
api: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
page_form: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => {
|
||||
return {
|
||||
pageNumber: 1,
|
||||
pageSize: 10,
|
||||
totalCount: 0,
|
||||
searchKeyword: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
json: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
img: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
page_form: {
|
||||
handler(newVal, oldVal) {
|
||||
this.localPageForm = {...newVal}
|
||||
},
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
tableLoading: false,
|
||||
tableFinished: false,
|
||||
tableRefreshing: false,
|
||||
localPageForm: {...this.page_form}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
calcTitle(row) {
|
||||
if (!this.title) return ""
|
||||
// 处理key1.key2.key3 这样的形式
|
||||
const keys = this.title.split(".")
|
||||
|
||||
let val = JSON.parse(JSON.stringify(row));
|
||||
for (let key of keys) {
|
||||
if (val == null || typeof val !== 'object') {
|
||||
return "";
|
||||
}
|
||||
val = val[key];
|
||||
}
|
||||
return val == null ? "" : val;
|
||||
},
|
||||
|
||||
onLoad() {
|
||||
if (this.tableFinished) return
|
||||
this.localPageForm.pageNumber++
|
||||
this.$emit("update:page_form", {...this.localPageForm})
|
||||
this.pageData()
|
||||
},
|
||||
|
||||
pageData() {
|
||||
this.tableLoading = true
|
||||
|
||||
const loading = createListLoading()
|
||||
this.$axios.post(this.api, this.json ? ({pageForm: JSON.stringify(this.localPageForm)}) : this.localPageForm).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableData = this.tableData.concat(res.data.list)
|
||||
this.localPageForm.totalCount = res.data.totalCount
|
||||
if (this.tableData.length >= this.localPageForm.totalCount) {
|
||||
this.tableFinished = true
|
||||
}
|
||||
this.$emit("update:page_form", {...this.localPageForm})
|
||||
}
|
||||
}).finally(() => {
|
||||
loading.close()
|
||||
console.log(this.tableLoading)
|
||||
this.tableLoading = false
|
||||
console.log(this.tableLoading)
|
||||
this.tableRefreshing = false
|
||||
})
|
||||
},
|
||||
doSearch() {
|
||||
this.tableFinished = false
|
||||
this.tableData = []
|
||||
this.localPageForm.pageNumber = 1
|
||||
this.$emit("update:page_form", {...this.localPageForm})
|
||||
this.pageData()
|
||||
},
|
||||
onRefresh() {
|
||||
this.localPageForm.pageNumber = 1
|
||||
this.$emit("update:page_form", {...this.localPageForm})
|
||||
this.doSearch()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$emit("ready")
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.table-list-container {
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item {
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #eaecef;
|
||||
padding: 16px;
|
||||
position: relative;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item:last-child {
|
||||
border-bottom: none;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .item-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .item-title {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #1f2f3d;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
flex: 1;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .img-container {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
flex-shrink: 0;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .img-container img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
|
||||
.table-list-container .table-list-item .item-meta {
|
||||
display: flex;
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
margin-bottom: 5px;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .meta-item i {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
width: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .item-content {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .item-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 12px;
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #eaecef;
|
||||
color: #909399;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .item-actions {
|
||||
display: flex;
|
||||
column-gap: 20px;
|
||||
justify-content: end;
|
||||
margin-top: 15px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid #eaecef;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .action-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
font-size: 14px;
|
||||
color: var(--color-primary);
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .action-btn i {
|
||||
margin-right: 6px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .action-btn.delete {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.table-list-container .table-list-item .action-btn.review {
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.table-list-container .empty-state {
|
||||
text-align: center;
|
||||
padding: 60px 20px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.table-list-container .empty-state i {
|
||||
font-size: 60px;
|
||||
margin-bottom: 16px;
|
||||
color: #dcdee0;
|
||||
}
|
||||
|
||||
.table-list-container .empty-state p {
|
||||
margin-top: 10px;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,167 @@
|
||||
<template>
|
||||
<div style="position: relative !important">
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-autocomplete
|
||||
v-model="addressValue"
|
||||
style="width: 100%"
|
||||
:fetch-suggestions="querySearchAsync"
|
||||
placeholder="请输入关键词查询地址"
|
||||
@select="handleSelect"
|
||||
/>
|
||||
</div>
|
||||
<div id="mapContainer"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
let marker = null
|
||||
let circle = null
|
||||
module.exports = {
|
||||
name: "mapContainer",
|
||||
props: {
|
||||
isCircle: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
radius: {
|
||||
type: Number,
|
||||
default: 50
|
||||
},
|
||||
position: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
view: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 此处不声明 map 对象,可以直接使用 this.map赋值或者采用非响应式的普通对象来存储。
|
||||
map: null,
|
||||
poi: this.position,
|
||||
appMapCenterPointX: 0,
|
||||
appMapCenterPointY: 0,
|
||||
addressValue: "",
|
||||
placeSearchComponent: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect(item) {
|
||||
if (item.location) {
|
||||
// 定位到中心点
|
||||
this.map.setCenter(item.location)
|
||||
// TODO 获取数据,对数据进行操作如:添加marker等
|
||||
this.clearMarker()
|
||||
this.createMarker(item.location)
|
||||
this.poi = [item.location.lng, item.location.lat]
|
||||
}
|
||||
},
|
||||
querySearchAsync(queryString, cb) {
|
||||
if (!queryString) {
|
||||
return
|
||||
}
|
||||
this.placeSearchComponent.search(queryString, function (status, result) {
|
||||
// 查询成功时,result即对应匹配的POI信息
|
||||
if (status !== "error") {
|
||||
result.poiList.pois.forEach((item) => {
|
||||
item.value = item.name + "【详细地址:" + item.address + "】"
|
||||
})
|
||||
cb(result.poiList.pois)
|
||||
}
|
||||
})
|
||||
},
|
||||
initMap() {
|
||||
this.map = new AMap.Map("mapContainer", {
|
||||
// 设置地图容器id
|
||||
resizeEnable: true,
|
||||
zoom: 16, // 初始化地图级别
|
||||
center: [this.appMapCenterPointX, this.appMapCenterPointY] // 初始化地图中心点位置
|
||||
})
|
||||
console.log(this.map)
|
||||
this.placeSearchComponent = new AMap.PlaceSearch({
|
||||
// city 指定搜索所在城市,支持传入格式有:城市名、citycode和adcode
|
||||
city: ""
|
||||
})
|
||||
this.clearMarker()
|
||||
if (this.poi && this.poi.length > 0) {
|
||||
const coordinateArray = this.poi
|
||||
this.createMarker(coordinateArray)
|
||||
} else {
|
||||
this.createMarker([this.appMapCenterPointX, this.appMapCenterPointY])
|
||||
}
|
||||
this.map.on("click", (e) => {
|
||||
if (!this.view) {
|
||||
this.clearMarker()
|
||||
const position = [e.lnglat.getLng(), e.lnglat.getLat()]
|
||||
this.createMarker(position)
|
||||
this.poi = position
|
||||
}
|
||||
})
|
||||
},
|
||||
// 清楚签到点位
|
||||
clearMarker() {
|
||||
if (marker) {
|
||||
this.map.remove(marker)
|
||||
}
|
||||
if (circle && this.isCircle) {
|
||||
this.map.remove(circle)
|
||||
}
|
||||
},
|
||||
// 创建签到点位
|
||||
createMarker(position) {
|
||||
if (this.isCircle) {
|
||||
circle = new AMap.Circle({
|
||||
center: new AMap.LngLat(position[0], position[1]), // 圆心位置
|
||||
radius: this.radius, // 半径
|
||||
strokeColor: "#F33", // 线颜色
|
||||
strokeOpacity: 1, // 线透明度
|
||||
strokeWeight: 1, // 线粗细度
|
||||
fillColor: "#ee2200", // 填充颜色
|
||||
fillOpacity: 0.35 // 填充透明度
|
||||
})
|
||||
this.map.add(circle)
|
||||
this.getAddress(position)
|
||||
this.map.setFitView()
|
||||
}
|
||||
marker = new AMap.Marker({
|
||||
position: position,
|
||||
offset: new AMap.Pixel(0, 0)
|
||||
})
|
||||
this.map.add(marker)
|
||||
this.getAddress(position)
|
||||
this.map.setFitView(null, false, [150, 60, 100, 60])
|
||||
},
|
||||
getAddress() {},
|
||||
async getConfigKey(key) {
|
||||
const resp = await this.$axios.post("/open/common/getConfigKey", { key })
|
||||
return resp.data
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
position(newVal) {
|
||||
this.poi = newVal
|
||||
},
|
||||
poi(newVal) {
|
||||
this.$emit("update:position", newVal)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
// DOM初始化完成进行地图初始化
|
||||
this.$nextTick(async () => {
|
||||
this.appMapCenterPointX = await this.getConfigKey("AppMapCenterPointX")
|
||||
this.appMapCenterPointY = await this.getConfigKey("AppMapCenterPointY")
|
||||
this.initMap()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#mapContainer {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div style="position: relative !important">
|
||||
<div style="margin-bottom: 10px">
|
||||
<el-autocomplete
|
||||
v-model="addressValue"
|
||||
style="width: 100%"
|
||||
:fetch-suggestions="querySearchAsync"
|
||||
placeholder="请输入关键词查询地址"
|
||||
@select="handleSelect"
|
||||
:trigger-on-focus="false"
|
||||
/>
|
||||
</div>
|
||||
<div id="mapContainer"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "TMap",
|
||||
props: {
|
||||
radius: {
|
||||
type: Number,
|
||||
default: 50
|
||||
},
|
||||
position: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
view: { type: Boolean, default: false }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
map: null,
|
||||
poi: this.position,
|
||||
appMapCenterPointX: 0,
|
||||
appMapCenterPointY: 0,
|
||||
markerLayer: null,
|
||||
addressValue: '',
|
||||
circle: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleSelect(item) {
|
||||
if (item.location) {
|
||||
// 定位到中心点
|
||||
const center = new TMap.LatLng(item.location.lat, item.location.lng)
|
||||
this.map.setCenter(center)
|
||||
this.clearMarker()
|
||||
this.createMarker(center)
|
||||
this.poi = [item.location.lat, item.location.lng]
|
||||
}
|
||||
},
|
||||
async querySearchAsync(queryString, cb) {
|
||||
if (!queryString) {
|
||||
return
|
||||
}
|
||||
const res = await this.$axios.post("/platform/TMap/suggestion", {
|
||||
keyword: queryString,
|
||||
})
|
||||
if (res.code === 0 && res.data.status === 0) {
|
||||
res.data.data.forEach((item) => {
|
||||
item.value = item.title + "【详细地址:" + item.address + "】"
|
||||
})
|
||||
cb(res.data.data)
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
cb([])
|
||||
}
|
||||
},
|
||||
initMap() {
|
||||
const center = new TMap.LatLng(this.appMapCenterPointY, this.appMapCenterPointX)
|
||||
this.map = new TMap.Map('mapContainer', {
|
||||
resizeEnable: true,
|
||||
zoom: 16,
|
||||
center: center
|
||||
})
|
||||
// 初始化
|
||||
this.markerLayer = new TMap.MultiMarker({
|
||||
map: this.map,
|
||||
geometries: []
|
||||
});
|
||||
if (this.poi && this.poi.length > 0) {
|
||||
const posi = new TMap.LatLng(this.poi[0], this.poi[1])
|
||||
this.createMarker(posi)
|
||||
this.map.setCenter(posi)
|
||||
} else {
|
||||
this.createMarker(center)
|
||||
}
|
||||
this.map.on("click", (event) => {
|
||||
if (!this.view) {
|
||||
this.clearMarker()
|
||||
this.createMarker(event.latLng)
|
||||
this.poi = [event.latLng.getLat(), event.latLng.getLng()]
|
||||
}
|
||||
})
|
||||
},
|
||||
// 清除签到点位
|
||||
clearMarker() {
|
||||
if (this.markerLayer) {
|
||||
this.markerLayer.setGeometries([])
|
||||
}
|
||||
if(this.circle) {
|
||||
this.circle.setGeometries([])
|
||||
}
|
||||
},
|
||||
// 创建签到点位
|
||||
createMarker(position) {
|
||||
this.markerLayer.add([
|
||||
{
|
||||
id: 'marker_' + Date.now(),
|
||||
position: position
|
||||
}
|
||||
]);
|
||||
this.circle = new TMap.MultiCircle({
|
||||
map: this.map,
|
||||
geometries: [{
|
||||
center: position,
|
||||
radius: this.radius,
|
||||
}],
|
||||
});
|
||||
},
|
||||
async getConfigKey(key) {
|
||||
const resp = await this.$axios.post("/open/common/getConfigKey", { key })
|
||||
return resp.data
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
position(newVal) {
|
||||
this.poi = newVal
|
||||
},
|
||||
poi(newVal) {
|
||||
this.$emit("update:position", newVal)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
// DOM初始化完成进行地图初始化
|
||||
this.$nextTick(async () => {
|
||||
this.appMapCenterPointX = await this.getConfigKey("AppMapCenterPointX")
|
||||
this.appMapCenterPointY = await this.getConfigKey("AppMapCenterPointY")
|
||||
this.initMap()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
#mapContainer {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 500px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
components: {
|
||||
"drawer-user-scope": httpVueLoader("/components/module/activity/DrawerUserScope.vue")
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: [String, Number],
|
||||
default: null
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
groupOptions: [],
|
||||
internalGroupId: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
internalGroupId(newValue) {
|
||||
this.$emit('update:value', newValue);
|
||||
},
|
||||
value:{
|
||||
handler:function (val){
|
||||
this.internalGroupId = val
|
||||
},
|
||||
immediate:true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async listGroups() {
|
||||
const {data} = await $.get("/platform/activity/basic/scope/getActivityUserScopeGroup")
|
||||
this.groupOptions = data
|
||||
},
|
||||
onGroupChange(id) {
|
||||
this.listGroups()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.listGroups()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div style="display: flex;">
|
||||
<el-select placeholder="参加人员范围"
|
||||
v-model="internalGroupId"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
filterable>
|
||||
<el-option v-for="item in groupOptions"
|
||||
:label="item.groupName"
|
||||
:value="item.groupId"
|
||||
:key="item.groupId"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-button @click="$refs.drawerUserScope.userScopeDialog = true" type="primary" icon="el-icon-setting" class="ml10">设置
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<drawer-user-scope
|
||||
@group_change="onGroupChange"
|
||||
ref="drawerUserScope"
|
||||
:group_id.sync="internalGroupId"
|
||||
></drawer-user-scope>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "snakerChart",
|
||||
data() {
|
||||
return {
|
||||
designVisible: false,
|
||||
designUrl: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpenFull(processDefineId,instanceId) {
|
||||
this.designVisible = true
|
||||
this.designUrl = `/flow/define/preview?defineId=${processDefineId}&instanceId=${instanceId}`
|
||||
},
|
||||
onOpen(processDefineKey){
|
||||
this.designVisible = true
|
||||
this.designUrl = `/flow/define/preview?defineKey=${processDefineKey}`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<el-dialog title="流程图预览" :visible.sync="designVisible" fullscreen width="80%" top="50px" append-to-body>
|
||||
<div class="diagram-viewer">
|
||||
<iframe v-if="designVisible" :src="designUrl" frameborder="0"></iframe>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.diagram-viewer {
|
||||
/*height: 65vh;*/
|
||||
height: calc(100vh - 60px - 55px);
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.diagram-viewer iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,775 @@
|
||||
<template>
|
||||
<div class="flow-container">
|
||||
<!-- 页面标题栏 -->
|
||||
<div class="page-header">
|
||||
<div class="header-content">
|
||||
<div class="title-section">
|
||||
<h2 class="page-title">{{ pageTitle }}</h2>
|
||||
<div class="page-breadcrumb">
|
||||
<span>{{ isNewApplication ? "发起申请" : "任务处理" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-section">
|
||||
<span class="status-label">流程状态:</span>
|
||||
<span class="status-badge" :class="statusClass">{{ processStatusText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 主要内容区域 -->
|
||||
<div class="main-content">
|
||||
<!-- 左侧内容 -->
|
||||
<div class="content-left">
|
||||
<!-- 流程基本信息 -->
|
||||
<div class="info-panel">
|
||||
<div class="panel-header">
|
||||
<h3 class="panel-title">流程信息</h3>
|
||||
<div class="panel-actions">
|
||||
<el-button size="small" type="text" @click="showProcessDiagram" v-if="isNewApplication">
|
||||
<i class="el-icon-view"></i>
|
||||
查看流程图
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<div class="info-table">
|
||||
<!-- 新申请场景 -->
|
||||
<template v-if="isNewApplication">
|
||||
<div class="info-row">
|
||||
<div class="info-label">流程名称:</div>
|
||||
<div class="info-value">{{ processDefinition.displayName }}</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">流程说明:</div>
|
||||
<div class="info-value">
|
||||
<div v-html="processDefinition.description"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 已有流程场景 -->
|
||||
<template v-else-if="isExistingProcess">
|
||||
<div class="info-row">
|
||||
<div class="info-label">流程名称:</div>
|
||||
<div class="info-value">{{ processInstance.displayName }}</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">发起人:</div>
|
||||
<div class="info-value">{{ processInstance?.ext?.initiatorName || "-" }}</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">发起时间:</div>
|
||||
<div class="info-value">{{ formatDate(processInstance.createdAt) }}</div>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<div class="info-label">流程进度:</div>
|
||||
<div class="info-value">
|
||||
<span @click="showProcessDiagram" style="color: var(--color-primary); cursor: pointer">查看流程图</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 历史办理过程面板 -->
|
||||
<div v-if="shouldShowHistoryProcess" class="history-panel">
|
||||
<div class="panel-header">
|
||||
<h3 class="panel-title">详细办理过程</h3>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<div id="history-process-container" class="form-content">
|
||||
<div v-if="pjaxLoading.historyProcess" class="loading-state">
|
||||
<i class="el-icon-loading"></i>
|
||||
<span>正在加载历史办理过程...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 表单处理区域 -->
|
||||
<div class="form-panel" v-if="isNewApplication || todoTasks.map((v) => v.id).includes(taskId)">
|
||||
<div class="panel-header">
|
||||
<h3 class="panel-title">{{ formPanelTitle }}</h3>
|
||||
</div>
|
||||
<div class="panel-content">
|
||||
<!-- 新申请表单 -->
|
||||
<div v-if="isNewApplication" id="application-form-container" class="form-content">
|
||||
<div v-if="pjaxLoading.apply" class="loading-state">
|
||||
<i class="el-icon-loading"></i>
|
||||
<span>正在加载申请表单...</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 当前任务表单 -->
|
||||
<div v-if="shouldShowTaskForm" id="task-form-container" class="form-content">
|
||||
<div v-if="pjaxLoading.taskForm" class="loading-state">
|
||||
<i class="el-icon-loading"></i>
|
||||
<span>正在加载任务表单...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧审批记录 -->
|
||||
<!-- <div class="content-right" v-if="isExistingProcess">-->
|
||||
<!-- <snaker-flow-history-approval :task_id="taskId"></snaker-flow-history-approval>-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
|
||||
<!-- 流程图对话框 -->
|
||||
<el-dialog title="流程图预览" :visible.sync="designVisible" width="80%" top="8vh">
|
||||
<div class="diagram-viewer">
|
||||
<iframe v-if="designVisible" :src="designUrl" frameborder="0"></iframe>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script type="text/javascript">
|
||||
class PjaxSync {
|
||||
constructor() {
|
||||
this.isLoading = false
|
||||
this.queue = []
|
||||
}
|
||||
|
||||
async request(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const requestData = { url, options, resolve, reject }
|
||||
|
||||
if (this.isLoading) {
|
||||
this.queue.push(requestData)
|
||||
return
|
||||
}
|
||||
|
||||
this._executeRequest(requestData)
|
||||
})
|
||||
}
|
||||
|
||||
_executeRequest({ url, options, resolve, reject }) {
|
||||
this.isLoading = true
|
||||
|
||||
const defaultOptions = {
|
||||
timeout: 10000,
|
||||
push: false,
|
||||
replace: false,
|
||||
...options
|
||||
}
|
||||
|
||||
const successHandler = (event, data, status, xhr) => {
|
||||
this._cleanup()
|
||||
resolve({ data, status, xhr })
|
||||
this._processQueue()
|
||||
}
|
||||
|
||||
const errorHandler = (event, xhr, textStatus, errorThrown) => {
|
||||
this._cleanup()
|
||||
reject(new Error(textStatus || "PJAX request failed"))
|
||||
this._processQueue()
|
||||
}
|
||||
|
||||
$(document).one("pjax:success", successHandler)
|
||||
$(document).one("pjax:error", errorHandler)
|
||||
|
||||
$.pjax({
|
||||
url: url,
|
||||
...defaultOptions
|
||||
})
|
||||
}
|
||||
|
||||
_cleanup() {
|
||||
this.isLoading = false
|
||||
$(document).off("pjax:success pjax:error")
|
||||
}
|
||||
|
||||
_processQueue() {
|
||||
if (this.queue.length > 0) {
|
||||
const nextRequest = this.queue.shift()
|
||||
this._executeRequest(nextRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 创建全局实例
|
||||
const pjaxSync = new PjaxSync()
|
||||
|
||||
module.exports = {
|
||||
name: "SnakerFlow",
|
||||
data() {
|
||||
return {
|
||||
// URL参数
|
||||
taskId: GetQueryString("taskId") ? parseInt(GetQueryString("taskId")) : null,
|
||||
instanceId: GetQueryString("instanceId"),
|
||||
businessId: GetQueryString("businessId"),
|
||||
defineKey: GetQueryString("defineKey"), // 新增:流程定义KEY
|
||||
|
||||
processInstance: {},
|
||||
//流程定义信息
|
||||
processDefinition: {},
|
||||
todoTasks: [],
|
||||
currentTask: {},
|
||||
|
||||
// 加载状态
|
||||
loading: false,
|
||||
// 错误信息
|
||||
error: null,
|
||||
|
||||
// PJAX 加载状态
|
||||
pjaxLoading: {
|
||||
apply: false,
|
||||
taskForm: false,
|
||||
historyProcess: false
|
||||
},
|
||||
|
||||
// 显示历史记录
|
||||
showHistory: false,
|
||||
|
||||
// 流程图相关
|
||||
designVisible: false,
|
||||
designUrl: "",
|
||||
|
||||
// 流程状态枚举映射
|
||||
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
|
||||
},
|
||||
|
||||
// 任务类型枚举
|
||||
performTypeEnum: {
|
||||
NORMAL: 0, // 普通任务
|
||||
COUNTERSIGN: 1 // 会签任务
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 页面标题
|
||||
pageTitle() {
|
||||
if (this.isNewApplication) {
|
||||
return this.processDefinition.displayName || this.processDefinition.name || "发起申请"
|
||||
} else {
|
||||
return this.processInstance.displayName || "任务处理"
|
||||
}
|
||||
},
|
||||
|
||||
// 流程状态文本
|
||||
processStatusText() {
|
||||
if (this.isNewApplication) {
|
||||
return "待提交"
|
||||
} else {
|
||||
return this.processStatusMap[this.processInstance.state]?.text || "未知状态"
|
||||
}
|
||||
},
|
||||
|
||||
// 状态样式类
|
||||
statusClass() {
|
||||
if (this.isNewApplication) {
|
||||
return "status-pending"
|
||||
} else {
|
||||
const statusClass = this.processStatusMap[this.processInstance.state]?.class
|
||||
return `status-${statusClass || "unknown"}`
|
||||
}
|
||||
},
|
||||
|
||||
// 表单面板标题
|
||||
formPanelTitle() {
|
||||
if (this.isNewApplication) {
|
||||
return "填写申请"
|
||||
} else if (this.shouldShowTaskForm) {
|
||||
return this.currentTask.displayName || "任务表单"
|
||||
} else if (this.shouldShowHistoryProcess) {
|
||||
return "申请表单"
|
||||
} else {
|
||||
return "表单信息"
|
||||
}
|
||||
},
|
||||
|
||||
// 是否显示历史办理过程
|
||||
shouldShowHistoryProcess() {
|
||||
return this.instanceId !== null
|
||||
},
|
||||
|
||||
// 是否显示任务表单
|
||||
shouldShowTaskForm() {
|
||||
return this.taskId != null
|
||||
},
|
||||
|
||||
// 判断是否为新申请
|
||||
isNewApplication() {
|
||||
return !this.taskId && !this.instanceId && this.defineKey
|
||||
},
|
||||
|
||||
// 判断是否为已有流程实例
|
||||
isExistingProcess() {
|
||||
return this.taskId || this.instanceId
|
||||
},
|
||||
|
||||
// 判断任务是否进行中
|
||||
isTaskInProgress() {
|
||||
return this.currentTask && this.currentTask.taskState === this.taskStateEnum.DOING
|
||||
},
|
||||
|
||||
// 判断是否为第一个任务节点
|
||||
isFirstTaskNode() {
|
||||
return this.currentTask && this.currentTask.ext && this.currentTask.ext.isFirstTaskNode
|
||||
},
|
||||
|
||||
// 流程图预览URL
|
||||
processDesignUrl() {
|
||||
debugger
|
||||
if (this.isNewApplication && this.processDefinition.id) {
|
||||
return `/flow/define/preview?defineId=${this.processDefinition.id}`
|
||||
} else if (this.isExistingProcess && this.processInstance.processDefineId) {
|
||||
return `/flow/define/preview?defineId=${this.processInstance.processDefineId}&instanceId=${this.instanceId}`
|
||||
}
|
||||
return ""
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initComponent()
|
||||
// 监听消息
|
||||
new BroadcastChannel("zhgh-global-channel").addEventListener("message", (event) => {
|
||||
if (event.data.type === "task-complete") {
|
||||
this.initComponent()
|
||||
}
|
||||
})
|
||||
},
|
||||
beforeDestroy() {
|
||||
console.log("beforeDestroy")
|
||||
},
|
||||
methods: {
|
||||
// 初始化组件
|
||||
async initComponent() {
|
||||
this.loading = true
|
||||
if (this.isNewApplication) {
|
||||
// 新申请:加载流程定义信息
|
||||
await this.loadProcessDefinition()
|
||||
// 加载流程申请表单
|
||||
await this.loadApplicationForm()
|
||||
} else if (this.isExistingProcess) {
|
||||
// 已有流程:加载流程实例信息
|
||||
await this.loadProcessInstance()
|
||||
if (this.processInstance && this.processInstance.state === 30) {
|
||||
// 流程被撤回了 此时加载申请表单
|
||||
// 加载流程申请表单
|
||||
await this.loadApplicationForm()
|
||||
}
|
||||
|
||||
// 加载任务信息
|
||||
await this.loadCurrentTask()
|
||||
// 如果有businessId且不是第一个任务节点,加载历史办理过程
|
||||
if (this.businessId) {
|
||||
await this.loadHistoryProcess()
|
||||
}
|
||||
} else {
|
||||
console.warn("URL中缺少必要的参数(defineKey、taskId或instanceId),无法初始化组件")
|
||||
this.loading = false
|
||||
return
|
||||
}
|
||||
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
// 加载流程定义信息
|
||||
async loadProcessDefinition() {
|
||||
if (!this.defineKey) return
|
||||
try {
|
||||
const response = await $.get("/flow/common/defineInfo", {
|
||||
defineKey: this.defineKey,
|
||||
instanceId: this.instanceId
|
||||
})
|
||||
if (response.code === 0) {
|
||||
this.processDefinition = response.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载流程定义信息失败:", error)
|
||||
}
|
||||
},
|
||||
|
||||
// 加载申请表单
|
||||
async loadApplicationForm() {
|
||||
if (!this.defineKey || !this.processDefinition.instanceUrl) return
|
||||
|
||||
this.pjaxLoading.apply = true
|
||||
|
||||
await pjaxSync.request(this.processDefinition.instanceUrl, {
|
||||
container: "#application-form-container"
|
||||
})
|
||||
|
||||
this.pjaxLoading.apply = false
|
||||
},
|
||||
|
||||
// 加载流程实例信息
|
||||
async loadProcessInstance() {
|
||||
if (!this.instanceId) return
|
||||
const { code, data, msg } = await $.get("/flow/common/instanceInfo", { instanceId: this.instanceId })
|
||||
if (code === 0) {
|
||||
this.processInstance = data.processInstance
|
||||
this.processDefinition = data.processInstance?.jsonObject
|
||||
this.todoTasks = data.todoTasks
|
||||
}
|
||||
},
|
||||
|
||||
// 加载任务信息
|
||||
async loadCurrentTask() {
|
||||
if (!this.taskId) return
|
||||
$.get("/flow/common/taskInfo", { taskId: this.taskId }, (response) => {
|
||||
if (response.code === 0) {
|
||||
this.currentTask = response.data
|
||||
this.loadTaskForm()
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 加载任务表单
|
||||
async loadTaskForm() {
|
||||
if (!this.taskId || !this.currentTask?.taskModel?.form) return
|
||||
|
||||
if (!this.todoTasks.map((v) => v.id).includes(this.taskId)) {
|
||||
return
|
||||
}
|
||||
|
||||
this.pjaxLoading.taskForm = true
|
||||
|
||||
await pjaxSync.request(this.currentTask.taskModel.form, {
|
||||
container: "#task-form-container"
|
||||
})
|
||||
},
|
||||
|
||||
// 加载历史办理过程
|
||||
async loadHistoryProcess() {
|
||||
if (!this.businessId) return
|
||||
const { instanceViewUrl } = this.processInstance.jsonObject
|
||||
if (instanceViewUrl) {
|
||||
this.pjaxLoading.historyProcess = true
|
||||
|
||||
await pjaxSync.request(`${instanceViewUrl}?businessId=${this.businessId}&instanceId=${this.instanceId}`, {
|
||||
container: "#history-process-container"
|
||||
})
|
||||
|
||||
this.pjaxLoading.historyProcess = false
|
||||
}
|
||||
},
|
||||
|
||||
// 显示流程图
|
||||
showProcessDiagram() {
|
||||
debugger
|
||||
if (this.processDesignUrl) {
|
||||
this.designUrl = this.processDesignUrl
|
||||
this.designVisible = true
|
||||
}
|
||||
},
|
||||
|
||||
// 获取状态文本
|
||||
getStatusText(state) {
|
||||
return this.processStatusMap[state]?.text || "未知状态"
|
||||
},
|
||||
|
||||
// 获取状态样式类
|
||||
getStatusClass(state) {
|
||||
return `status-${this.processStatusMap[state]?.class || "unknown"}`
|
||||
},
|
||||
|
||||
// 格式化日期
|
||||
formatDate(dateStr) {
|
||||
if (!dateStr) return "-"
|
||||
return new Date(dateStr).toLocaleString("zh-CN", {
|
||||
year: "numeric",
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 简洁OA风格样式 */
|
||||
.flow-container {
|
||||
background-color: #f5f6fa;
|
||||
min-height: 100vh;
|
||||
font-family: "Microsoft YaHei", Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* 页面头部 - 固定在顶部 */
|
||||
.page-header {
|
||||
background: white;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
padding: 16px 0;
|
||||
position: fixed;
|
||||
top: 64px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 19000;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
/* 主要内容区域 - 添加顶部间距 */
|
||||
.main-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
margin-top: 80px; /* 为固定头部留出空间 */
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.title-section .page-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
|
||||
.page-breadcrumb {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.separator {
|
||||
margin: 0 6px;
|
||||
}
|
||||
|
||||
.status-section {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.status-label {
|
||||
font-size: 14px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background: #fdf6ec;
|
||||
color: #e6a23c;
|
||||
}
|
||||
|
||||
.status-doing {
|
||||
background: #ecf5ff;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.status-finished {
|
||||
background: #f0f9ff;
|
||||
color: #67c23a;
|
||||
}
|
||||
|
||||
.status-withdraw {
|
||||
background: #f4f4f5;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.status-interrupt {
|
||||
background: #fef0f0;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.status-reject {
|
||||
background: #fef0f0;
|
||||
color: #f56c6c;
|
||||
}
|
||||
|
||||
.content-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.content-right {
|
||||
width: 320px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* 面板样式 */
|
||||
.info-panel,
|
||||
.form-panel,
|
||||
.history-panel,
|
||||
.approval-panel {
|
||||
background: white;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.panel-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.panel-content.no-padding {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 信息表格 */
|
||||
.info-table {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f5f7fa;
|
||||
}
|
||||
|
||||
.info-row:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
min-width: 80px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
color: #303133;
|
||||
font-size: 14px;
|
||||
flex: 1;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 表单内容 */
|
||||
.form-content {
|
||||
min-height: 200px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* 加载状态 */
|
||||
.loading-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 60px 20px;
|
||||
color: #909399;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.loading-state i {
|
||||
font-size: 20px;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.loading-state span {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* 流程图查看器 */
|
||||
.diagram-viewer {
|
||||
height: 65vh;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.diagram-viewer iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1024px) {
|
||||
.main-content {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.content-right {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.main-content {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.panel-content {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.info-row {
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,380 @@
|
||||
<template>
|
||||
<div class="approval-sidebar">
|
||||
<div class="sidebar-header">
|
||||
<h4>审批记录</h4>
|
||||
</div>
|
||||
<div class="timeline-container" v-if="historyApprovalRecords.length > 0">
|
||||
<div class="timeline-wrapper">
|
||||
<div
|
||||
v-for="(record, index) in historyApprovalRecords"
|
||||
:key="record.id"
|
||||
class="timeline-item">
|
||||
|
||||
<!-- 时间线节点 -->
|
||||
<div class="timeline-node">
|
||||
<div class="timeline-dot" :class="getDotClass(record.taskState)"></div>
|
||||
<div class="timeline-line" v-if="index < historyApprovalRecords.length - 1"></div>
|
||||
</div>
|
||||
|
||||
<!-- 审批内容 -->
|
||||
<div class="timeline-content">
|
||||
<div class="approval-card">
|
||||
<div class="card-header">
|
||||
<span class="task-name">{{ record.displayName }}</span>
|
||||
<span class="approval-time">{{ formatTimestamp(record.finishTime || record.createdAt) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="info-item" v-if="record.operator && record.operator !== 'flow.auto'">
|
||||
<span class="label">办理人:</span>
|
||||
<span class="value">{{ getOperatorName(record) }}</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item" v-if="record.ext && record.ext.submitType">
|
||||
<span class="label">操作:</span>
|
||||
<span class="value submit-type" :class="'submit-' + record.ext.submitType">
|
||||
{{ getSubmitTypeText(record.ext.submitType) }}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="info-item" v-if="record.ext && record.ext.opinion">
|
||||
<span class="label">意见:</span>
|
||||
<span class="value">{{ record.ext.opinion }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-records">
|
||||
暂无审批记录
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "SnakerFlowHisApproval",
|
||||
props: {
|
||||
task_id: {
|
||||
type: String | Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
// 历史审批记录
|
||||
historyApprovalRecords: [],
|
||||
|
||||
// 加载状态
|
||||
loading: false,
|
||||
|
||||
// 任务状态枚举
|
||||
taskStateEnum: {
|
||||
DOING: 10,
|
||||
FINISHED: 20,
|
||||
WITHDRAW: 30,
|
||||
INTERRUPT: 40,
|
||||
PENDING: 50,
|
||||
ABANDON: 99
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 获取实例ID(从task_id获取流程实例信息)
|
||||
instanceId() {
|
||||
return GetQueryString("instanceId")
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadHistoryApprovalRecords()
|
||||
},
|
||||
watch: {
|
||||
task_id: {
|
||||
handler(newVal) {
|
||||
if (newVal) {
|
||||
this.loadHistoryApprovalRecords()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载历史审批记录
|
||||
async loadHistoryApprovalRecords() {
|
||||
if (!this.instanceId && !this.task_id) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const params = this.instanceId ?
|
||||
{ instanceId: this.instanceId } :
|
||||
{ taskId: this.task_id }
|
||||
|
||||
const response = await $.post("/flow/common/approvalRecord", params)
|
||||
if (response.code === 0) {
|
||||
this.historyApprovalRecords = response.data || []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("加载历史审批记录失败:", error)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化时间戳
|
||||
formatTimestamp(timestamp) {
|
||||
if (!timestamp) return ""
|
||||
const date = new Date(timestamp)
|
||||
return date.toLocaleString("zh-CN", {
|
||||
month: "2-digit",
|
||||
day: "2-digit",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit"
|
||||
})
|
||||
},
|
||||
|
||||
// 获取操作人姓名
|
||||
getOperatorName(record) {
|
||||
if (!record.operator || record.operator === 'flow.auto') {
|
||||
return "系统自动"
|
||||
}
|
||||
if (record.ext && record.ext.operatorName) {
|
||||
return record.ext.operatorName
|
||||
}
|
||||
return record.operator
|
||||
},
|
||||
|
||||
// 获取提交类型文本
|
||||
getSubmitTypeText(submitType) {
|
||||
const typeMap = {
|
||||
'agree': '同意',
|
||||
'reject': '拒绝',
|
||||
'back': '退回',
|
||||
'transfer': '转办',
|
||||
'delegate': '委托',
|
||||
'submit': '提交'
|
||||
}
|
||||
return typeMap[submitType] || submitType
|
||||
},
|
||||
|
||||
// 获取节点样式类
|
||||
getDotClass(state) {
|
||||
switch (state) {
|
||||
case this.taskStateEnum.FINISHED:
|
||||
return "dot-success"
|
||||
case this.taskStateEnum.WITHDRAW:
|
||||
return "dot-warning"
|
||||
case this.taskStateEnum.INTERRUPT:
|
||||
case this.taskStateEnum.ABANDON:
|
||||
return "dot-danger"
|
||||
case this.taskStateEnum.PENDING:
|
||||
return "dot-info"
|
||||
case this.taskStateEnum.DOING:
|
||||
return "dot-primary"
|
||||
default:
|
||||
return "dot-default"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.approval-sidebar {
|
||||
width: 320px;
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
height: fit-content;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.sidebar-header {
|
||||
background: #f5f5f5;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 12px 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.sidebar-header h4 {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.timeline-container {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.timeline-wrapper {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-node {
|
||||
position: relative;
|
||||
margin-right: 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid #fff;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.dot-success {
|
||||
background: #52c41a;
|
||||
}
|
||||
|
||||
.dot-warning {
|
||||
background: #faad14;
|
||||
}
|
||||
|
||||
.dot-danger {
|
||||
background: #f5222d;
|
||||
}
|
||||
|
||||
.dot-primary {
|
||||
background: #1890ff;
|
||||
}
|
||||
|
||||
.dot-info {
|
||||
background: #999;
|
||||
}
|
||||
|
||||
.dot-default {
|
||||
background: #d9d9d9;
|
||||
}
|
||||
|
||||
.timeline-line {
|
||||
width: 1px;
|
||||
background: #e8e8e8;
|
||||
flex: 1;
|
||||
margin-top: 4px;
|
||||
min-height: 30px;
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.approval-card {
|
||||
background: #fff;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: #fafafa;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.task-name {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.approval-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
margin-bottom: 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.info-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.label {
|
||||
color: #666;
|
||||
min-width: 50px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.value {
|
||||
color: #333;
|
||||
flex: 1;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.submit-type {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.submit-agree {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.submit-reject {
|
||||
color: #f5222d;
|
||||
}
|
||||
|
||||
.submit-back {
|
||||
color: #faad14;
|
||||
}
|
||||
|
||||
.submit-transfer, .submit-delegate, .submit-submit {
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.no-records {
|
||||
padding: 40px 20px;
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.timeline-container::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.timeline-container::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
}
|
||||
|
||||
.timeline-container::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
}
|
||||
|
||||
.timeline-container::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,315 @@
|
||||
<template>
|
||||
<div class="snaker-task-action" v-if="!loading || taskInfo">
|
||||
<!-- 指定下一节点处理人 -->
|
||||
<div class="next-handler-section" v-if="supportSpecifyNextHandler && candidates.length > 0">
|
||||
<el-checkbox v-model="specifyNextHandler" @change="onSpecifyNextHandlerChange">指定下一节点处理人</el-checkbox>
|
||||
<div class="handler-select-wrapper" v-if="specifyNextHandler">
|
||||
<el-select v-model="selectedHandler" placeholder="请选择处理人" clearable filterable class="handler-select">
|
||||
<el-option v-for="candidate in candidates" :key="candidate.userId" :label="candidate.userName" :value="candidate.userId">
|
||||
<span style="float: left">{{ candidate.userName }}({{ candidate.ext?.loginName }})</span>
|
||||
<span style="float: right; color: #8492a6; font-size: 13px">{{ candidate.ext?.unitName }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 第一个任务节点或新申请:显示提交、保存草稿、取消 -->
|
||||
<!-- <template v-if="isFirstTaskNodeOrNew">-->
|
||||
<!-- <el-button type="primary" @click="handleSubmit" :loading="actionLoading" icon="el-icon-upload2">提交</el-button>-->
|
||||
<!-- <!– 保存草稿只在流程未发起时显示 –>-->
|
||||
<!-- <el-button @click="handleSaveDraft" v-if="!actionLoading && show_save_draft && !instanceId" icon="el-icon-document">保存草稿</el-button>-->
|
||||
<!-- <el-button @click="handleCancel" :loading="actionLoading" v-if="show_cancel" icon="el-icon-close">取消</el-button>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!-- <!– 其他任务节点:显示审批按钮 –>-->
|
||||
<!-- <template v-else>-->
|
||||
<!-- <!– 会签任务按钮 –>-->
|
||||
<!-- <template v-if="is_countersign_task">-->
|
||||
<!-- <el-button type="primary" @click="handleAction('AGREE')" :loading="actionLoading" icon="el-icon-check">同意</el-button>-->
|
||||
<!-- <el-button type="danger" plain @click="handleAction('COUNTERSIGN_DISAGREE')" :loading="actionLoading" icon="el-icon-close">-->
|
||||
<!-- 不同意-->
|
||||
<!-- </el-button>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!-- <!– 普通任务按钮 –>-->
|
||||
<!-- <template v-else>-->
|
||||
<!-- <el-button type="primary" @click="handleAction('AGREE')" :loading="actionLoading" icon="el-icon-check">同意</el-button>-->
|
||||
<!-- <el-button type="danger" plain @click="handleAction('REJECT')" :loading="actionLoading" icon="el-icon-close">拒绝</el-button>-->
|
||||
<!-- <el-button @click="handleAction('ROLLBACK')" :loading="actionLoading" icon="el-icon-back">退回上一步</el-button>-->
|
||||
<!-- <el-button @click="handleAction('ROLLBACK_TO_OPERATOR')" :loading="actionLoading" icon="el-icon-d-arrow-left">退回发起人</el-button>-->
|
||||
<!-- <el-button @click="handleAction('JUMP')" :loading="actionLoading" icon="el-icon-position">跳转</el-button>-->
|
||||
<!-- </template>-->
|
||||
<!-- </template>-->
|
||||
|
||||
<!--没有流程实例的时候-->
|
||||
<template v-if="!instanceId">
|
||||
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||||
<el-button @click="handleSaveDraft">保存</el-button>
|
||||
</template>
|
||||
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button v-for="item in buttons" @click="handleAction(item.value)" :type="item.theme" :icon="item.icon" size="medium" :key="item.value">
|
||||
{{ item.replacementText || item.originalText }}
|
||||
</el-button>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- 加载状态 -->
|
||||
<div v-else class="snaker-task-action loading-state">
|
||||
<el-button loading icon="el-icon-loading">加载中...</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "SnakerFlowTaskFormAction",
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
// 加载状态
|
||||
loading: false,
|
||||
// 操作按钮加载状态
|
||||
actionLoading: false,
|
||||
// 任务信息
|
||||
taskInfo: null,
|
||||
// 任务操作枚举
|
||||
actionEnum: {
|
||||
APPLY: "0", // 发起申请
|
||||
AGREE: "1", // 同意
|
||||
REJECT: "2", // 拒绝
|
||||
COUNTERSIGN_DISAGREE: "20", // 会签不同意 拒绝申请
|
||||
ROLLBACK: "3", // 退回上一步
|
||||
ROLLBACK_TO_OPERATOR: "6", // 退回发起人
|
||||
JUMP: "4", // 跳转
|
||||
RE_APPLY: "5", // 重新提交
|
||||
SUBMIT: "0" // 发起申请
|
||||
},
|
||||
// 任务类型枚举
|
||||
performTypeEnum: {
|
||||
NORMAL: 0, // 普通任务
|
||||
COUNTERSIGN: 1 // 会签任务
|
||||
},
|
||||
// 指定下一节点处理人相关
|
||||
specifyNextHandler: false, // 是否指定下一节点处理人
|
||||
selectedHandler: null, // 选中的处理人ID
|
||||
candidates: [], // 候选人列表
|
||||
supportCandidate: false,
|
||||
|
||||
taskId: GetQueryString("taskId"),
|
||||
instanceId: GetQueryString("instanceId"),
|
||||
defineKey: GetQueryString("defineKey"),
|
||||
defineId: GetQueryString("defineId"),
|
||||
businessId: GetQueryString("businessId")
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 是否为会签任务
|
||||
is_countersign_task() {
|
||||
return this.taskInfo && this.taskInfo.performType === this.performTypeEnum.COUNTERSIGN
|
||||
},
|
||||
|
||||
// 是否为第一个任务节点或新申请
|
||||
isFirstTaskNodeOrNew() {
|
||||
// 没有任务ID,说明是新申请
|
||||
if (!this.taskId) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 有任务ID但是任务信息中标识为第一个节点
|
||||
return this.taskInfo && this.taskInfo.ext && this.taskInfo.ext.isFirstTaskNode
|
||||
},
|
||||
|
||||
// 审批按钮配置
|
||||
buttons() {
|
||||
if (this.taskInfo) {
|
||||
return this.taskInfo.taskModel.ext.buttonConfig.filter((v) => v.visible)
|
||||
}
|
||||
return []
|
||||
},
|
||||
|
||||
// 是否支持选择下一步处理人
|
||||
supportSpecifyNextHandler() {
|
||||
return this.taskInfo && this.taskInfo.taskModel.ext.enableNextOperator
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// 只有当有任务ID时才加载任务信息
|
||||
if (this.taskId) {
|
||||
this.loadTaskInfo()
|
||||
this.loadCandidates()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 加载任务信息
|
||||
loadTaskInfo() {
|
||||
if (!this.taskId) return
|
||||
|
||||
this.loading = true
|
||||
this.taskInfo = null
|
||||
|
||||
$.get("/flow/common/taskInfo", {
|
||||
taskId: this.taskId
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.taskInfo = res.data
|
||||
}
|
||||
})
|
||||
.always(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
|
||||
// 处理提交操作
|
||||
handleSubmit() {
|
||||
// 根据是否有流程实例来确定操作类型
|
||||
const submitAction = this.instanceId ? this.actionEnum.RE_APPLY : this.actionEnum.APPLY
|
||||
|
||||
// 触发父组件事件,传递提交操作
|
||||
this.$emit("task-action", {
|
||||
submitType: submitAction,
|
||||
taskId: this.taskId,
|
||||
instanceId: this.instanceId,
|
||||
defineId: this.defineId,
|
||||
defineKey: this.defineKey,
|
||||
businessId: this.businessId
|
||||
})
|
||||
},
|
||||
|
||||
// 处理任务操作
|
||||
handleAction(actionKey) {
|
||||
if (this.actionLoading || !this.taskInfo) return
|
||||
|
||||
// 构建操作数据
|
||||
const actionData = {
|
||||
submitType: this.actionEnum[actionKey],
|
||||
taskId: this.taskId,
|
||||
processTaskId: this.taskId,
|
||||
instanceId: this.instanceId,
|
||||
defineId: this.defineId,
|
||||
defineKey: this.defineKey,
|
||||
businessId: this.businessId
|
||||
}
|
||||
|
||||
// 如果指定了下一节点处理人,添加到操作数据中
|
||||
if (this.specifyNextHandler && this.selectedHandler) {
|
||||
actionData.tf_nextNodeOperator = this.selectedHandler
|
||||
}
|
||||
|
||||
// 触发父组件事件,传递操作类型和任务信息
|
||||
this.$emit("task-action", actionData)
|
||||
},
|
||||
|
||||
// 保存草稿
|
||||
handleSaveDraft() {
|
||||
if (this.actionLoading) return
|
||||
|
||||
this.$emit("save-draft", {
|
||||
submitType: this.actionEnum.APPLY,
|
||||
defineKey: this.defineKey,
|
||||
defineId: this.defineId
|
||||
})
|
||||
},
|
||||
|
||||
// 取消操作
|
||||
handleCancel() {
|
||||
const func = () => window.close()
|
||||
this.$emit("cancel", func)
|
||||
if (!this.$listeners.cancel) {
|
||||
func()
|
||||
}
|
||||
},
|
||||
|
||||
// 刷新任务信息
|
||||
refresh() {
|
||||
if (this.taskId) {
|
||||
this.loadTaskInfo()
|
||||
}
|
||||
},
|
||||
|
||||
// 处理指定下一节点处理人勾选框变化
|
||||
onSpecifyNextHandlerChange(checked) {
|
||||
if (!checked) {
|
||||
this.selectedHandler = null
|
||||
}
|
||||
},
|
||||
|
||||
// 加载候选人列表
|
||||
loadCandidates() {
|
||||
$.get("/flow/common/candidate", {
|
||||
taskId: this.taskId
|
||||
}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.candidates = res.data.candidates
|
||||
this.supportCandidate = res.data.support
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.snaker-task-action {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #eee;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.snaker-task-action .el-button {
|
||||
margin: 0 8px;
|
||||
min-width: 80px;
|
||||
}
|
||||
|
||||
.loading-state {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
min-height: 60px;
|
||||
}
|
||||
|
||||
/* 指定下一节点处理人样式 */
|
||||
.next-handler-section {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 4px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.handler-select-wrapper {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.handler-select {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.snaker-task-action {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.snaker-task-action .el-button {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.next-handler-section {
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.handler-select {
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,35 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "snakerStart",
|
||||
props: {
|
||||
define_key:{
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
openChart() {
|
||||
this.$refs.snakerChartRef.onOpen(this.define_key)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<el-row type="flex" justify="space-between" slot="header">
|
||||
<h3 style="color: var(--color-primary)">{{ label }}</h3>
|
||||
<el-link type="primary" @click="openChart">流程图</el-link>
|
||||
</el-row>
|
||||
<snaker-chart ref="snakerChartRef"></snaker-chart>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,349 @@
|
||||
<!--<template>-->
|
||||
<!-- <div>-->
|
||||
<!-- <div class="ant-spin-nested-loading" v-if="spinning && $slots.default">-->
|
||||
<!-- <div class="ant-spin ant-spin-spinning" :class="{ 'ant-spin-lg': size === 'large', 'ant-spin-small': size === 'small' }">-->
|
||||
<!-- <span class="ant-spin-dot ant-spin-dot-spin">-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- </span>-->
|
||||
<!-- <div class="ant-spin-text" v-if="$props.tip">-->
|
||||
<!-- {{ tip }}-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="ant-spin-container" :class="{ 'ant-spin-blur': spinning }">-->
|
||||
<!-- <slot></slot>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<!-- <div class="ant-spin ant-spin-spinning" :class="{ 'ant-spin-lg': size === 'large', 'ant-spin-small': size === 'small' }" v-else>-->
|
||||
<!-- <span class="ant-spin-dot ant-spin-dot-spin">-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- <i class="ant-spin-dot-item"></i>-->
|
||||
<!-- </span>-->
|
||||
<!-- <div class="ant-spin-text" v-if="$props.tip">-->
|
||||
<!-- {{ tip }}-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!-- </div>-->
|
||||
<!--</template>-->
|
||||
|
||||
<!--<script>-->
|
||||
<!--module.exports = {-->
|
||||
<!-- name: "index",-->
|
||||
<!-- props: {-->
|
||||
<!-- size: String,-->
|
||||
<!-- tip: String,-->
|
||||
<!-- spinning: Boolean-->
|
||||
<!-- },-->
|
||||
<!-- computed: {}-->
|
||||
<!--}-->
|
||||
<!--</script>-->
|
||||
|
||||
<template>
|
||||
<!-- <div :class="spinClassName" :style="style">-->
|
||||
<!-- <div v-if="renderIndicator" class="ant-spin-dot">-->
|
||||
<!-- <span class="ant-spin-dot-item"></span>-->
|
||||
<!-- <span class="ant-spin-dot-item"></span>-->
|
||||
<!-- <span class="ant-spin-dot-item"></span>-->
|
||||
<!-- <span class="ant-spin-dot-item"></span>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div v-if="tip" class="ant-spin-text">{{ tip }}</div>-->
|
||||
<!-- <slot></slot>-->
|
||||
<!-- </div>-->
|
||||
<div class="ant-spin-nested-loading" data-v-2f42c11b="">
|
||||
<div>
|
||||
<div class="ant-spin ant-spin-spinning">
|
||||
<span class="ant-spin-dot ant-spin-dot-spin">
|
||||
<i class="ant-spin-dot-item"></i>
|
||||
<i class="ant-spin-dot-item"></i>
|
||||
<i class="ant-spin-dot-item"></i>
|
||||
<i class="ant-spin-dot-item"></i>
|
||||
</span>
|
||||
<!---->
|
||||
</div>
|
||||
</div>
|
||||
<div class="ant-spin-container ant-spin-blur">
|
||||
<el-alert>
|
||||
<p>22222222222222</p>
|
||||
<p>22222222222222</p>
|
||||
<p>22222222222222</p>
|
||||
<p>22222222222222</p>
|
||||
<p>22222222222222</p>
|
||||
</el-alert>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "EL_SPIN",
|
||||
props: {
|
||||
spinning: {
|
||||
type: Boolean,
|
||||
default: undefined
|
||||
},
|
||||
size: String,
|
||||
wrapperClassName: String,
|
||||
tip: String,
|
||||
delay: Number,
|
||||
indicator: String
|
||||
},
|
||||
computed: {
|
||||
spinClassName() {
|
||||
const { size, spinning } = this
|
||||
return {
|
||||
[`ant-spin`]: true,
|
||||
[`ant-spin-sm`]: size === "small",
|
||||
[`ant-spin-lg`]: size === "large",
|
||||
[`ant-spin-spinning`]: spinning,
|
||||
[`ant-spin-show-text`]: !!this.tip,
|
||||
[`ant-spin-rtl`]: this.direction === "rtl"
|
||||
}
|
||||
},
|
||||
renderIndicator() {
|
||||
// const indicator = getComponent(this, "indicator")
|
||||
// return indicator !== null && !Array.isArray(indicator)
|
||||
return true
|
||||
},
|
||||
style() {
|
||||
return this.$attrs.style
|
||||
},
|
||||
direction() {
|
||||
// return this.configProvider.direction
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ant-spin {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: #000000d9;
|
||||
font-size: 14px;
|
||||
font-variant: tabular-nums;
|
||||
line-height: 1.5715;
|
||||
list-style: none;
|
||||
font-feature-settings: tnum;
|
||||
position: absolute;
|
||||
display: none;
|
||||
color: #1890ff;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
opacity: 0;
|
||||
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
|
||||
}
|
||||
|
||||
.ant-spin-spinning {
|
||||
position: static;
|
||||
display: inline-block;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin .ant-spin-dot {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -10px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin .ant-spin-text {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
width: 100%;
|
||||
padding-top: 5px;
|
||||
text-shadow: 0 1px 2px #fff;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin.ant-spin-show-text .ant-spin-dot {
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-sm .ant-spin-dot {
|
||||
margin: -7px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-sm .ant-spin-text {
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-sm.ant-spin-show-text .ant-spin-dot {
|
||||
margin-top: -17px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-lg .ant-spin-dot {
|
||||
margin: -16px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-lg .ant-spin-text {
|
||||
padding-top: 11px;
|
||||
}
|
||||
|
||||
.ant-spin-nested-loading > div > .ant-spin-lg.ant-spin-show-text .ant-spin-dot {
|
||||
margin-top: -26px;
|
||||
}
|
||||
|
||||
.ant-spin-container {
|
||||
position: relative;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.ant-spin-container:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
display: none \;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
opacity: 0;
|
||||
transition: all 0.3s;
|
||||
content: "";
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ant-spin-blur {
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
opacity: 0.5;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ant-spin-blur:after {
|
||||
opacity: 0.4;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.ant-spin-tip {
|
||||
color: #00000073;
|
||||
}
|
||||
|
||||
.ant-spin-dot {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
background-color: #1890ff;
|
||||
border-radius: 100%;
|
||||
transform: scale(0.75);
|
||||
transform-origin: 50% 50%;
|
||||
opacity: 0.3;
|
||||
animation: antSpinMove 1s infinite linear alternate;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(1) {
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(2) {
|
||||
top: 0;
|
||||
right: 0;
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(3) {
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.ant-spin-dot-item:nth-child(4) {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
.ant-spin-dot-spin {
|
||||
transform: rotate(45deg);
|
||||
animation: antRotate 1.2s infinite linear;
|
||||
}
|
||||
|
||||
.ant-spin-sm .ant-spin-dot {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ant-spin-sm .ant-spin-dot i {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
|
||||
.ant-spin-lg .ant-spin-dot {
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.ant-spin-lg .ant-spin-dot i {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
.ant-spin.ant-spin-show-text .ant-spin-text {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
|
||||
.ant-spin-blur {
|
||||
background: #fff;
|
||||
opacity: 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antSpinMove {
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes antRotate {
|
||||
to {
|
||||
transform: rotate(405deg);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-spin-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
.ant-spin-rtl .ant-spin-dot-spin {
|
||||
transform: rotate(-45deg);
|
||||
animation-name: antRotateRtl;
|
||||
}
|
||||
|
||||
@keyframes antRotateRtl {
|
||||
to {
|
||||
transform: rotate(-405deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
class SysDictData {
|
||||
constructor(dict) {
|
||||
this.dict = dict
|
||||
}
|
||||
|
||||
async init(dictNames) {
|
||||
const ps = []
|
||||
dictNames.forEach((name) => {
|
||||
Vue.set(this.dict.type, name, null)
|
||||
ps.push(
|
||||
$.get("/open/common/dictOptions", { code: name }).then((res) => {
|
||||
const dictValue = res.data.map((v) => {
|
||||
return {
|
||||
name: v.name,
|
||||
text: v.name,
|
||||
label: v.name,
|
||||
code: v.code,
|
||||
raw: v
|
||||
}
|
||||
})
|
||||
this.dict.type[name] = Object.freeze(dictValue)
|
||||
})
|
||||
)
|
||||
})
|
||||
await Promise.all(ps)
|
||||
}
|
||||
}
|
||||
|
||||
window.dictData = {}
|
||||
window.dictData.install = function (Vue) {
|
||||
Vue.mixin({
|
||||
data() {
|
||||
if (this.$options.dicts instanceof Array && this.$options.dicts.length > 0) {
|
||||
return { dict: { type: {} } }
|
||||
} else {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (this.$options.dicts instanceof Array && this.$options.dicts.length > 0) {
|
||||
new SysDictData(this.dict).init(this.$options.dicts).then()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (window.Vue) {
|
||||
Vue.use(window.dictData)
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
<template>
|
||||
<el-select
|
||||
v-model="valueAsString"
|
||||
:placeholder="placeholder"
|
||||
@change="onChange"
|
||||
:disabled="disabled"
|
||||
:clearable="clearable"
|
||||
:multiple="multiple"
|
||||
:filterable="filterable"
|
||||
:size="size"
|
||||
style="width: 100%"
|
||||
v-bind="$attrs"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
v-if="!item.disabled"
|
||||
:key="item[option_value]"
|
||||
:label="item[option_label]"
|
||||
:value="item[option_value]"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
value: { type: [String, Array] },
|
||||
code: {
|
||||
type: [String, Array],
|
||||
default: ""
|
||||
},
|
||||
option_value: {
|
||||
type: String,
|
||||
default: "code"
|
||||
},
|
||||
option_label: {
|
||||
type: String,
|
||||
default: "name"
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请选择"
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
multiple: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
filterable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "change"
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: []
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
valueAsString: {
|
||||
get() {
|
||||
if (Array.isArray(this.value)) {
|
||||
return this.value
|
||||
} else {
|
||||
return this.value ? this.value.toString() : ""
|
||||
}
|
||||
},
|
||||
set(val) {}
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
code(val) {
|
||||
this.flushOptions()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(val) {
|
||||
this.$emit("change", val)
|
||||
},
|
||||
async flushOptions() {
|
||||
if (!this.code) {
|
||||
this.options = []
|
||||
return
|
||||
}
|
||||
$.get("/open/common/dictOptions", { code: this.code }).then((res) => {
|
||||
this.options = res.data
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.flushOptions()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="(item, index) in options">
|
||||
<template v-if="values.includes(item[option_value])">
|
||||
<span :key="item.value">{{ item[option_label] }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "DictTag",
|
||||
props: {
|
||||
options: {
|
||||
type: Array,
|
||||
default: null
|
||||
},
|
||||
option_value: {
|
||||
type: String,
|
||||
default: "code"
|
||||
},
|
||||
option_label: {
|
||||
type: String,
|
||||
default: "name"
|
||||
},
|
||||
value: [Number, String, Array]
|
||||
},
|
||||
computed: {
|
||||
values() {
|
||||
if (this.value !== null && typeof this.value !== "undefined") {
|
||||
return Array.isArray(this.value) ? this.value : [String(this.value)]
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<el-dialog title="查看" :visible.sync="dialogVisible" width="60%">
|
||||
<form-create :value.sync="dynamicFormData" v-model="fapi" :rule="formCreateRule" :option="formCreateOption" disabled></form-create>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "dynamic-Table-form-eval",
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: false,
|
||||
dynamicFormData: {},
|
||||
formCreateRule: [],
|
||||
formCreateOption: {},
|
||||
fapi: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onOpen(formConfig, dynamicFormData) {
|
||||
debugger
|
||||
this.dialogVisible = true
|
||||
this.$nextTick(() => {
|
||||
this.formCreateRule = formCreate.parseJson(formConfig.rule)
|
||||
this.formCreateOption = formCreate.parseJson(formConfig.options)
|
||||
if (typeof dynamicFormData === "string") {
|
||||
try {
|
||||
this.dynamicFormData = JSON.parse(dynamicFormData)
|
||||
} catch (e) {}
|
||||
} else if (typeof dynamicFormData === "object") {
|
||||
this.dynamicFormData = dynamicFormData
|
||||
}
|
||||
this.fapi.disabled(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,118 @@
|
||||
<template>
|
||||
<div>
|
||||
<template v-for="item in options">
|
||||
<component :key="item[value_key]" v-if="item[value_key] === value" v-bind="$attrs"
|
||||
:is="isMobile ? 'van-tag' : 'el-tag'" :type="item.type"
|
||||
>{{ item[label_key] }}
|
||||
</component>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 全局缓存和请求Promise缓存
|
||||
const enumCache = {}
|
||||
const requestPromises = {}
|
||||
|
||||
module.exports = {
|
||||
name: "DictTag",
|
||||
props: {
|
||||
value: { type: String | Number },
|
||||
name: { type: String },
|
||||
value_key: {
|
||||
type: String,
|
||||
default: "code"
|
||||
},
|
||||
label_key: {
|
||||
type: String,
|
||||
default: "name"
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
options: [],
|
||||
isMobile: true,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
code: {
|
||||
handler(val) {
|
||||
this.getEnumOptions()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
isMobileDevice() {
|
||||
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(
|
||||
navigator.userAgent
|
||||
);
|
||||
},
|
||||
getEnumOptions() {
|
||||
// // 检查数据缓存
|
||||
// if (enumCache[this.name]) {
|
||||
// this.options = enumCache[this.name]
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// // 检查是否已有相同请求在进行中
|
||||
// if (requestPromises[this.name]) {
|
||||
// requestPromises[this.name].then(data => {
|
||||
// this.options = data
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
// 创建请求Promise并缓存
|
||||
requestPromises[this.name] = $.get("/open/common/dictEnumOptions", { name: this.name })
|
||||
.then(res => {
|
||||
if (res.code === 0) {
|
||||
// 存入数据缓存
|
||||
enumCache[this.name] = res.data
|
||||
// 清除请求Promise缓存
|
||||
delete requestPromises[this.name]
|
||||
return res.data
|
||||
}
|
||||
})
|
||||
|
||||
requestPromises[this.name].then(data => {
|
||||
this.options = data
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.isMobile = this.isMobileDevice()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.el-tag + .el-tag {
|
||||
margin-left: 10px;
|
||||
}
|
||||
.van-tag {
|
||||
height: 24px;
|
||||
padding: 0 8px;
|
||||
line-height: 22px;
|
||||
}
|
||||
.van-tag--success {
|
||||
background-color: #f0f9eb;
|
||||
border-color: #e1f3d8;
|
||||
color: #67c23a;
|
||||
}
|
||||
.van-tag--primary {
|
||||
color: var(--color-primary);
|
||||
background-color: #ecf5ff;
|
||||
border-color: #d9ecff;
|
||||
}
|
||||
.van-tag--danger{
|
||||
background-color: #fef0f0;
|
||||
border-color: #fde2e2;
|
||||
color: #f56c6c;
|
||||
}
|
||||
.van-tag--warning{
|
||||
background-color: #fdf6ec;
|
||||
border-color: #faecd8;
|
||||
color: #e6a23c;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="file-preview-container" v-if="fileList && fileList.length > 0">
|
||||
<div class="file-item" v-for="item in fileList" :key="item.id">
|
||||
<img
|
||||
v-if="item.suffix === 'jpg' || item.suffix === 'png' || item.suffix === 'jpeg' || item.suffix === 'gif'"
|
||||
:src="item.thumbnail"
|
||||
@click.stop="$commonUtil.previewFile(item)"
|
||||
class="file-image"
|
||||
/>
|
||||
|
||||
<img
|
||||
v-else
|
||||
:src="$commonUtil.getFileItemShowIcon(item.suffix)"
|
||||
@click.stop="$commonUtil.previewFile(item)"
|
||||
class="file-image"
|
||||
/>
|
||||
|
||||
<div class="file-download" @click="$downLoad(item.downloadPath)">
|
||||
<i class="el-icon-download"></i>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty-container">
|
||||
<div class="empty-text">暂无</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "H5Index",
|
||||
props: {
|
||||
files: {
|
||||
type: [String, Array],
|
||||
default: undefined,
|
||||
required: false
|
||||
},
|
||||
complete_result: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.buildEchoFileObject(val)
|
||||
} else {
|
||||
this.fileList = []
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buildEchoFileObject(val) {
|
||||
let ids = []
|
||||
if (this.complete_result) {
|
||||
if (Array.isArray(val)) {
|
||||
ids = val.map((item) => {
|
||||
if (item.response.data.includes("=")) {
|
||||
return item?.response?.data.substring(item?.response?.data.lastIndexOf("=") + 1)
|
||||
} else {
|
||||
return item?.response?.data
|
||||
}
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
ids = JSON.parse(val).map((item) => {
|
||||
if (item.response.data.includes("=")) {
|
||||
return item?.response?.data.substring(item?.response?.data.lastIndexOf("=") + 1)
|
||||
} else {
|
||||
return item?.response?.data
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
this.fileList = []
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//那就是链接 没有直接存id的吧。。
|
||||
if (Array.isArray(val)) {
|
||||
ids = val.map((item) => {
|
||||
return item.substring(item.lastIndexOf("=") + 1)
|
||||
})
|
||||
} else {
|
||||
ids = val.split(",").map((item) => {
|
||||
return item.substring(item.lastIndexOf("=") + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
this.requestFullFile(ids)
|
||||
},
|
||||
|
||||
requestFullFile(ids) {
|
||||
this.$axios.post("/platform/sys/file/previewFileData", {ids: JSON.stringify(ids)}).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.fileList = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-preview-container {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item {
|
||||
aspect-ratio: 1;
|
||||
position: relative;
|
||||
transition: all 300ms;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #e9ecef;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item:active {
|
||||
background: #e9ecef;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.file-preview-container .file-item .file-image {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item .file-download {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: white;
|
||||
background-color: rgba(0, 0, 0, 0.7);
|
||||
border-radius: 0 0 8px 8px;
|
||||
font-size: 12px;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.empty-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 120px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 480px) {
|
||||
.file-preview-container {
|
||||
gap: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item .file-image {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item .file-download {
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="pdf" id="pdf-container"></div>
|
||||
<div v-else v-html="content" class="content"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "PdfIndex",
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
default: '',
|
||||
required: true
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
required: false
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
pdf: true,
|
||||
pdfObj: null,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
try {
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(this.content, 'text/html');
|
||||
const link = doc.querySelector('a');
|
||||
const href = link.getAttribute('href');
|
||||
if(!href) {
|
||||
this.pdf = false
|
||||
return
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
this.pdfObj = new Pdfh5('#pdf-container', {
|
||||
pdfurl: href,
|
||||
});
|
||||
this.pdfObj.on("complete", function () {
|
||||
const elements = document.querySelectorAll('[class*="canvasImg"]')
|
||||
let classArray = []
|
||||
elements.forEach(element => {
|
||||
classArray.push(element.getAttribute('src'))
|
||||
})
|
||||
elements.forEach((element,index) => {
|
||||
element.addEventListener('click', function() {
|
||||
vant.ImagePreview({
|
||||
images: classArray,
|
||||
startPosition: index,
|
||||
closeable: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}catch (e) {
|
||||
this.pdf = false
|
||||
}
|
||||
},
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="file-preview-container" v-if="fileList && fileList.length > 0">
|
||||
<div class="file-item" title="点击查看" v-for="item in fileList" :key="item.id">
|
||||
<el-image
|
||||
v-if="item.suffix === 'jpg' || item.suffix === 'png' || item.suffix === 'jpeg' || item.suffix === 'gif'"
|
||||
:src="item.thumbnail"
|
||||
:fit="'cover'"
|
||||
:style="{ width: '100%', height: '100%' }"
|
||||
@click.stop="$commonUtil.previewFile(item)"
|
||||
></el-image>
|
||||
|
||||
<el-image
|
||||
v-else
|
||||
:src="$commonUtil.getFileItemShowIcon(item.suffix)"
|
||||
:fit="'cover'"
|
||||
@click.stop="$commonUtil.previewFile(item)"
|
||||
:style="{ width: '100%', height: '100%' }"
|
||||
></el-image>
|
||||
|
||||
<div class="file-download" title="点击下载" @click="$downLoad(item.downloadPath)">
|
||||
<i class="el-icon-download"></i>
|
||||
{{ item.name }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-empty v-else description="暂无" :image-size="60"></el-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "sysFilePreview",
|
||||
props: {
|
||||
files: {
|
||||
type: [String, Array],
|
||||
default: undefined,
|
||||
required: false
|
||||
},
|
||||
complete_result: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
files: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.buildEchoFileObject(val)
|
||||
} else {
|
||||
this.fileList = []
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buildEchoFileObject(val) {
|
||||
let ids = []
|
||||
if (this.complete_result) {
|
||||
if (Array.isArray(val)) {
|
||||
ids = val.map((item) => {
|
||||
if (item.response.data.includes("=")) {
|
||||
return item?.response?.data.substring(item?.response?.data.lastIndexOf("=") + 1)
|
||||
} else {
|
||||
return item?.response?.data
|
||||
}
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
ids = JSON.parse(val).map((item) => {
|
||||
if (item.response.data.includes("=")) {
|
||||
return item?.response?.data.substring(item?.response?.data.lastIndexOf("=") + 1)
|
||||
} else {
|
||||
return item?.response?.data
|
||||
}
|
||||
})
|
||||
} catch (err) {
|
||||
this.fileList = []
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//那就是链接 没有直接存id的吧。。
|
||||
if (Array.isArray(val)) {
|
||||
ids = val.map((item) => {
|
||||
return item.substring(item.lastIndexOf("=") + 1)
|
||||
})
|
||||
} else {
|
||||
ids = val.split(",").map((item) => {
|
||||
return item.substring(item.lastIndexOf("=") + 1)
|
||||
})
|
||||
}
|
||||
}
|
||||
this.requestFullFile(ids)
|
||||
},
|
||||
|
||||
requestFullFile(ids) {
|
||||
this.$axios.post("/platform/sys/file/previewFileData", { ids: JSON.stringify(ids) }).then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.fileList = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.file-preview-container {
|
||||
position: relative;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
column-gap: 10px;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item {
|
||||
width: 118px;
|
||||
height: 118px;
|
||||
position: relative;
|
||||
transition: all 500ms;
|
||||
border-radius: 5px;
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
column-gap: 15px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.file-preview-container .file-item:hover {
|
||||
background: rgb(230, 230, 230);
|
||||
}
|
||||
|
||||
.file-preview-container .file-item .el-image {
|
||||
width: 90px !important;
|
||||
height: 100%;
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
.file-preview-container .file-item .file-download {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: white;
|
||||
background-color: rgb(160, 160, 160);
|
||||
border-radius: 2px;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
.el-empty {
|
||||
padding: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,885 @@
|
||||
/**
|
||||
* 图标选择器基础数据
|
||||
* 推荐前往https://icones.js.org下载图标的Vue文件,然后放在src/assets/icons文件夹里面
|
||||
* 这个网址有118个图标集,包括antd、font awesome、bootstrap、eleme等累计140456个图标
|
||||
*/
|
||||
// const uiwIconComponentMap = import.meta.glob("../assets/icons/uiw/*.vue") // 异步方式
|
||||
//
|
||||
// const uiwIcons = Object.keys(uiwIconComponentMap).map((key) => {
|
||||
// return key.slice(key.lastIndexOf("/") + 1, key.lastIndexOf("."))
|
||||
// })
|
||||
|
||||
window.iconSelectConfig = {
|
||||
icons: [
|
||||
{
|
||||
name: "基础",
|
||||
key: "default",
|
||||
iconItem: [
|
||||
{
|
||||
name: "线框风格",
|
||||
key: "default",
|
||||
item: [
|
||||
"ant-design_account-book-outlined",
|
||||
"ant-design_aim-outlined",
|
||||
"ant-design_alert-outlined",
|
||||
"ant-design_alibaba-outlined",
|
||||
"ant-design_align-center-outlined",
|
||||
"ant-design_align-left-outlined",
|
||||
"ant-design_align-right-outlined",
|
||||
"ant-design_alipay-circle-outlined",
|
||||
"ant-design_alipay-outlined",
|
||||
"ant-design_aliwangwang-outlined",
|
||||
"ant-design_aliyun-outlined",
|
||||
"ant-design_amazon-outlined",
|
||||
"ant-design_android-outlined",
|
||||
"ant-design_ant-cloud-outlined",
|
||||
"ant-design_ant-design-outlined",
|
||||
"ant-design_apartment-outlined",
|
||||
"ant-design_api-outlined",
|
||||
"ant-design_apple-outlined",
|
||||
"ant-design_appstore-add-outlined",
|
||||
"ant-design_appstore-outlined",
|
||||
"ant-design_area-chart-outlined",
|
||||
"ant-design_arrow-down-outlined",
|
||||
"ant-design_arrow-left-outlined",
|
||||
"ant-design_arrow-right-outlined",
|
||||
"ant-design_arrow-up-outlined",
|
||||
"ant-design_arrows-alt-outlined",
|
||||
"ant-design_audio-muted-outlined",
|
||||
"ant-design_audio-outlined",
|
||||
"ant-design_audit-outlined",
|
||||
"ant-design_backward-outlined",
|
||||
"ant-design_baidu-outlined",
|
||||
"ant-design_bank-outlined",
|
||||
"ant-design_bar-chart-outlined",
|
||||
"ant-design_barcode-outlined",
|
||||
"ant-design_bars-outlined",
|
||||
"ant-design_behance-outlined",
|
||||
"ant-design_behance-square-outlined",
|
||||
"ant-design_bell-outlined",
|
||||
"ant-design_bg-colors-outlined",
|
||||
"ant-design_bilibili-outlined",
|
||||
"ant-design_block-outlined",
|
||||
"ant-design_bold-outlined",
|
||||
"ant-design_book-outlined",
|
||||
"ant-design_border-bottom-outlined",
|
||||
"ant-design_border-horizontal-outlined",
|
||||
"ant-design_border-inner-outlined",
|
||||
"ant-design_border-left-outlined",
|
||||
"ant-design_border-outer-outlined",
|
||||
"ant-design_border-outlined",
|
||||
"ant-design_border-right-outlined",
|
||||
"ant-design_border-top-outlined",
|
||||
"ant-design_border-verticle-outlined",
|
||||
"ant-design_borderless-table-outlined",
|
||||
"ant-design_box-plot-outlined",
|
||||
"ant-design_branches-outlined",
|
||||
"ant-design_bug-outlined",
|
||||
"ant-design_build-outlined",
|
||||
"ant-design_bulb-outlined",
|
||||
"ant-design_calculator-outlined",
|
||||
"ant-design_calendar-outlined",
|
||||
"ant-design_camera-outlined",
|
||||
"ant-design_car-outlined",
|
||||
"ant-design_caret-down-outlined",
|
||||
"ant-design_caret-left-outlined",
|
||||
"ant-design_caret-right-outlined",
|
||||
"ant-design_caret-up-outlined",
|
||||
"ant-design_carry-out-outlined",
|
||||
"ant-design_check-circle-outlined",
|
||||
"ant-design_check-outlined",
|
||||
"ant-design_check-square-outlined",
|
||||
"ant-design_chrome-outlined",
|
||||
"ant-design_ci-circle-outlined",
|
||||
"ant-design_ci-outlined",
|
||||
"ant-design_clear-outlined",
|
||||
"ant-design_clock-circle-outlined",
|
||||
"ant-design_close-circle-outlined",
|
||||
"ant-design_close-outlined",
|
||||
"ant-design_close-square-outlined",
|
||||
"ant-design_cloud-download-outlined",
|
||||
"ant-design_cloud-outlined",
|
||||
"ant-design_cloud-server-outlined",
|
||||
"ant-design_cloud-sync-outlined",
|
||||
"ant-design_cloud-upload-outlined",
|
||||
"ant-design_cluster-outlined",
|
||||
"ant-design_code-outlined",
|
||||
"ant-design_code-sandbox-outlined",
|
||||
"ant-design_codepen-circle-outlined",
|
||||
"ant-design_codepen-outlined",
|
||||
"ant-design_coffee-outlined",
|
||||
"ant-design_column-height-outlined",
|
||||
"ant-design_column-width-outlined",
|
||||
"ant-design_comment-outlined",
|
||||
"ant-design_compass-outlined",
|
||||
"ant-design_compress-outlined",
|
||||
"ant-design_console-sql-outlined",
|
||||
"ant-design_contacts-outlined",
|
||||
"ant-design_container-outlined",
|
||||
"ant-design_control-outlined",
|
||||
"ant-design_copy-outlined",
|
||||
"ant-design_copyright-circle-outlined",
|
||||
"ant-design_copyright-outlined",
|
||||
"ant-design_credit-card-outlined",
|
||||
"ant-design_crown-outlined",
|
||||
"ant-design_customer-service-outlined",
|
||||
"ant-design_dash-outlined",
|
||||
"ant-design_dashboard-outlined",
|
||||
"ant-design_database-outlined",
|
||||
"ant-design_delete-column-outlined",
|
||||
"ant-design_delete-outlined",
|
||||
"ant-design_delete-row-outlined",
|
||||
"ant-design_delivered-procedure-outlined",
|
||||
"ant-design_deployment-unit-outlined",
|
||||
"ant-design_desktop-outlined",
|
||||
"ant-design_diff-outlined",
|
||||
"ant-design_dingding-outlined",
|
||||
"ant-design_dingtalk-outlined",
|
||||
"ant-design_disconnect-outlined",
|
||||
"ant-design_discord-outlined",
|
||||
"ant-design_dislike-outlined",
|
||||
"ant-design_docker-outlined",
|
||||
"ant-design_dollar-circle-outlined",
|
||||
"ant-design_dollar-outlined",
|
||||
"ant-design_dot-chart-outlined",
|
||||
"ant-design_dot-net-outlined",
|
||||
"ant-design_double-left-outlined",
|
||||
"ant-design_double-right-outlined",
|
||||
"ant-design_down-circle-outlined",
|
||||
"ant-design_down-outlined",
|
||||
"ant-design_down-square-outlined",
|
||||
"ant-design_download-outlined",
|
||||
"ant-design_drag-outlined",
|
||||
"ant-design_dribbble-outlined",
|
||||
"ant-design_dribbble-square-outlined",
|
||||
"ant-design_dropbox-outlined",
|
||||
"ant-design_edit-outlined",
|
||||
"ant-design_ellipsis-outlined",
|
||||
"ant-design_enter-outlined",
|
||||
"ant-design_environment-outlined",
|
||||
"ant-design_euro-circle-outlined",
|
||||
"ant-design_euro-outlined",
|
||||
"ant-design_exception-outlined",
|
||||
"ant-design_exclamation-circle-outlined",
|
||||
"ant-design_exclamation-outlined",
|
||||
"ant-design_expand-alt-outlined",
|
||||
"ant-design_expand-outlined",
|
||||
"ant-design_experiment-outlined",
|
||||
"ant-design_export-outlined",
|
||||
"ant-design_eye-invisible-outlined",
|
||||
"ant-design_eye-outlined",
|
||||
"ant-design_facebook-outlined",
|
||||
"ant-design_fall-outlined",
|
||||
"ant-design_fast-backward-outlined",
|
||||
"ant-design_fast-forward-outlined",
|
||||
"ant-design_field-binary-outlined",
|
||||
"ant-design_field-number-outlined",
|
||||
"ant-design_field-string-outlined",
|
||||
"ant-design_field-time-outlined",
|
||||
"ant-design_file-add-outlined",
|
||||
"ant-design_file-done-outlined",
|
||||
"ant-design_file-excel-outlined",
|
||||
"ant-design_file-exclamation-outlined",
|
||||
"ant-design_file-gif-outlined",
|
||||
"ant-design_file-image-outlined",
|
||||
"ant-design_file-jpg-outlined",
|
||||
"ant-design_file-markdown-outlined",
|
||||
"ant-design_file-outlined",
|
||||
"ant-design_file-pdf-outlined",
|
||||
"ant-design_file-ppt-outlined",
|
||||
"ant-design_file-protect-outlined",
|
||||
"ant-design_file-search-outlined",
|
||||
"ant-design_file-sync-outlined",
|
||||
"ant-design_file-text-outlined",
|
||||
"ant-design_file-unknown-outlined",
|
||||
"ant-design_file-word-outlined",
|
||||
"ant-design_file-zip-outlined",
|
||||
"ant-design_filter-outlined",
|
||||
"ant-design_fire-outlined",
|
||||
"ant-design_flag-outlined",
|
||||
"ant-design_folder-add-outlined",
|
||||
"ant-design_folder-open-outlined",
|
||||
"ant-design_folder-outlined",
|
||||
"ant-design_folder-view-outlined",
|
||||
"ant-design_font-colors-outlined",
|
||||
"ant-design_font-size-outlined",
|
||||
"ant-design_fork-outlined",
|
||||
"ant-design_form-outlined",
|
||||
"ant-design_format-painter-outlined",
|
||||
"ant-design_forward-outlined",
|
||||
"ant-design_frown-outlined",
|
||||
"ant-design_fullscreen-exit-outlined",
|
||||
"ant-design_fullscreen-outlined",
|
||||
"ant-design_function-outlined",
|
||||
"ant-design_fund-outlined",
|
||||
"ant-design_fund-projection-screen-outlined",
|
||||
"ant-design_fund-view-outlined",
|
||||
"ant-design_funnel-plot-outlined",
|
||||
"ant-design_gateway-outlined",
|
||||
"ant-design_gif-outlined",
|
||||
"ant-design_gift-outlined",
|
||||
"ant-design_github-outlined",
|
||||
"ant-design_gitlab-outlined",
|
||||
"ant-design_global-outlined",
|
||||
"ant-design_gold-outlined",
|
||||
"ant-design_google-outlined",
|
||||
"ant-design_google-plus-outlined",
|
||||
"ant-design_group-outlined",
|
||||
"ant-design_harmony-o-s-outlined",
|
||||
"ant-design_hdd-outlined",
|
||||
"ant-design_heart-outlined",
|
||||
"ant-design_heat-map-outlined",
|
||||
"ant-design_highlight-outlined",
|
||||
"ant-design_history-outlined",
|
||||
"ant-design_holder-outlined",
|
||||
"ant-design_home-outlined",
|
||||
"ant-design_hourglass-outlined",
|
||||
"ant-design_html5-outlined",
|
||||
"ant-design_idcard-outlined",
|
||||
"ant-design_ie-outlined",
|
||||
"ant-design_import-outlined",
|
||||
"ant-design_inbox-outlined",
|
||||
"ant-design_info-circle-outlined",
|
||||
"ant-design_info-outlined",
|
||||
"ant-design_insert-row-above-outlined",
|
||||
"ant-design_insert-row-below-outlined",
|
||||
"ant-design_insert-row-left-outlined",
|
||||
"ant-design_insert-row-right-outlined",
|
||||
"ant-design_instagram-outlined",
|
||||
"ant-design_insurance-outlined",
|
||||
"ant-design_interaction-outlined",
|
||||
"ant-design_issues-close-outlined",
|
||||
"ant-design_italic-outlined",
|
||||
"ant-design_java-outlined",
|
||||
"ant-design_java-script-outlined",
|
||||
"ant-design_key-outlined",
|
||||
"ant-design_kubernetes-outlined",
|
||||
"ant-design_laptop-outlined",
|
||||
"ant-design_layout-outlined",
|
||||
"ant-design_left-circle-outlined",
|
||||
"ant-design_left-outlined",
|
||||
"ant-design_left-square-outlined",
|
||||
"ant-design_like-outlined",
|
||||
"ant-design_line-chart-outlined",
|
||||
"ant-design_line-height-outlined",
|
||||
"ant-design_line-outlined",
|
||||
"ant-design_link-outlined",
|
||||
"ant-design_linkedin-outlined",
|
||||
"ant-design_linux-outlined",
|
||||
"ant-design_loading-3-quarters-outlined",
|
||||
"ant-design_loading-outlined",
|
||||
"ant-design_lock-outlined",
|
||||
"ant-design_login-outlined",
|
||||
"ant-design_logout-outlined",
|
||||
"ant-design_mac-command-outlined",
|
||||
"ant-design_mail-outlined",
|
||||
"ant-design_man-outlined",
|
||||
"ant-design_medicine-box-outlined",
|
||||
"ant-design_medium-outlined",
|
||||
"ant-design_medium-workmark-outlined",
|
||||
"ant-design_meh-outlined",
|
||||
"ant-design_menu-fold-outlined",
|
||||
"ant-design_menu-outlined",
|
||||
"ant-design_menu-unfold-outlined",
|
||||
"ant-design_merge-cells-outlined",
|
||||
"ant-design_merge-outlined",
|
||||
"ant-design_message-outlined",
|
||||
"ant-design_minus-circle-outlined",
|
||||
"ant-design_minus-outlined",
|
||||
"ant-design_minus-square-outlined",
|
||||
"ant-design_mobile-outlined",
|
||||
"ant-design_money-collect-outlined",
|
||||
"ant-design_monitor-outlined",
|
||||
"ant-design_moon-outlined",
|
||||
"ant-design_more-outlined",
|
||||
"ant-design_muted-outlined",
|
||||
"ant-design_node-collapse-outlined",
|
||||
"ant-design_node-expand-outlined",
|
||||
"ant-design_node-index-outlined",
|
||||
"ant-design_notification-outlined",
|
||||
"ant-design_number-outlined",
|
||||
"ant-design_one-to-one-outlined",
|
||||
"ant-design_open-a-i-outlined",
|
||||
"ant-design_ordered-list-outlined",
|
||||
"ant-design_paper-clip-outlined",
|
||||
"ant-design_partition-outlined",
|
||||
"ant-design_pause-circle-outlined",
|
||||
"ant-design_pause-outlined",
|
||||
"ant-design_pay-circle-outlined",
|
||||
"ant-design_percentage-outlined",
|
||||
"ant-design_phone-outlined",
|
||||
"ant-design_pic-center-outlined",
|
||||
"ant-design_pic-left-outlined",
|
||||
"ant-design_pic-right-outlined",
|
||||
"ant-design_picture-outlined",
|
||||
"ant-design_pie-chart-outlined",
|
||||
"ant-design_pinterest-outlined",
|
||||
"ant-design_play-circle-outlined",
|
||||
"ant-design_play-square-outlined",
|
||||
"ant-design_plus-circle-outlined",
|
||||
"ant-design_plus-outlined",
|
||||
"ant-design_plus-square-outlined",
|
||||
"ant-design_pound-circle-outlined",
|
||||
"ant-design_pound-outlined",
|
||||
"ant-design_poweroff-outlined",
|
||||
"ant-design_printer-outlined",
|
||||
"ant-design_product-outlined",
|
||||
"ant-design_profile-outlined",
|
||||
"ant-design_project-outlined",
|
||||
"ant-design_property-safety-outlined",
|
||||
"ant-design_pull-request-outlined",
|
||||
"ant-design_pushpin-outlined",
|
||||
"ant-design_python-outlined",
|
||||
"ant-design_qq-outlined",
|
||||
"ant-design_qrcode-outlined",
|
||||
"ant-design_question-circle-outlined",
|
||||
"ant-design_question-outlined",
|
||||
"ant-design_radar-chart-outlined",
|
||||
"ant-design_radius-bottomleft-outlined",
|
||||
"ant-design_radius-bottomright-outlined",
|
||||
"ant-design_radius-setting-outlined",
|
||||
"ant-design_radius-upleft-outlined",
|
||||
"ant-design_radius-upright-outlined",
|
||||
"ant-design_read-outlined",
|
||||
"ant-design_reconciliation-outlined",
|
||||
"ant-design_red-envelope-outlined",
|
||||
"ant-design_reddit-outlined",
|
||||
"ant-design_redo-outlined",
|
||||
"ant-design_reload-outlined",
|
||||
"ant-design_rest-outlined",
|
||||
"ant-design_retweet-outlined",
|
||||
"ant-design_right-circle-outlined",
|
||||
"ant-design_right-outlined",
|
||||
"ant-design_right-square-outlined",
|
||||
"ant-design_rise-outlined",
|
||||
"ant-design_robot-outlined",
|
||||
"ant-design_rocket-outlined",
|
||||
"ant-design_rollback-outlined",
|
||||
"ant-design_rotate-left-outlined",
|
||||
"ant-design_rotate-right-outlined",
|
||||
"ant-design_ruby-outlined",
|
||||
"ant-design_safety-certificate-outlined",
|
||||
"ant-design_safety-outlined",
|
||||
"ant-design_save-outlined",
|
||||
"ant-design_scan-outlined",
|
||||
"ant-design_schedule-outlined",
|
||||
"ant-design_scissor-outlined",
|
||||
"ant-design_search-outlined",
|
||||
"ant-design_security-scan-outlined",
|
||||
"ant-design_select-outlined",
|
||||
"ant-design_send-outlined",
|
||||
"ant-design_setting-outlined",
|
||||
"ant-design_shake-outlined",
|
||||
"ant-design_share-alt-outlined",
|
||||
"ant-design_shop-outlined",
|
||||
"ant-design_shopping-cart-outlined",
|
||||
"ant-design_shopping-outlined",
|
||||
"ant-design_shrink-outlined",
|
||||
"ant-design_signature-outlined",
|
||||
"ant-design_sisternode-outlined",
|
||||
"ant-design_sketch-outlined",
|
||||
"ant-design_skin-outlined",
|
||||
"ant-design_skype-outlined",
|
||||
"ant-design_slack-outlined",
|
||||
"ant-design_slack-square-outlined",
|
||||
"ant-design_sliders-outlined",
|
||||
"ant-design_small-dash-outlined",
|
||||
"ant-design_smile-outlined",
|
||||
"ant-design_snippets-outlined",
|
||||
"ant-design_solution-outlined",
|
||||
"ant-design_sort-ascending-outlined",
|
||||
"ant-design_sort-descending-outlined",
|
||||
"ant-design_sound-outlined",
|
||||
"ant-design_split-cells-outlined",
|
||||
"ant-design_spotify-outlined",
|
||||
"ant-design_star-outlined",
|
||||
"ant-design_step-backward-outlined",
|
||||
"ant-design_step-forward-outlined",
|
||||
"ant-design_stock-outlined",
|
||||
"ant-design_stop-outlined",
|
||||
"ant-design_strikethrough-outlined",
|
||||
"ant-design_subnode-outlined",
|
||||
"ant-design_sun-outlined",
|
||||
"ant-design_swap-left-outlined",
|
||||
"ant-design_swap-outlined",
|
||||
"ant-design_swap-right-outlined",
|
||||
"ant-design_switcher-outlined",
|
||||
"ant-design_sync-outlined",
|
||||
"ant-design_table-outlined",
|
||||
"ant-design_tablet-outlined",
|
||||
"ant-design_tag-outlined",
|
||||
"ant-design_tags-outlined",
|
||||
"ant-design_taobao-circle-outlined",
|
||||
"ant-design_taobao-outlined",
|
||||
"ant-design_team-outlined",
|
||||
"ant-design_thunderbolt-outlined",
|
||||
"ant-design_tik-tok-outlined",
|
||||
"ant-design_to-top-outlined",
|
||||
"ant-design_tool-outlined",
|
||||
"ant-design_trademark-circle-outlined",
|
||||
"ant-design_trademark-outlined",
|
||||
"ant-design_transaction-outlined",
|
||||
"ant-design_translation-outlined",
|
||||
"ant-design_trophy-outlined",
|
||||
"ant-design_truck-outlined",
|
||||
"ant-design_twitch-outlined",
|
||||
"ant-design_twitter-outlined",
|
||||
"ant-design_underline-outlined",
|
||||
"ant-design_undo-outlined",
|
||||
"ant-design_ungroup-outlined",
|
||||
"ant-design_unlock-outlined",
|
||||
"ant-design_unordered-list-outlined",
|
||||
"ant-design_up-circle-outlined",
|
||||
"ant-design_up-outlined",
|
||||
"ant-design_up-square-outlined",
|
||||
"ant-design_upload-outlined",
|
||||
"ant-design_usb-outlined",
|
||||
"ant-design_user-add-outlined",
|
||||
"ant-design_user-delete-outlined",
|
||||
"ant-design_user-outlined",
|
||||
"ant-design_user-switch-outlined",
|
||||
"ant-design_usergroup-add-outlined",
|
||||
"ant-design_usergroup-delete-outlined",
|
||||
"ant-design_verified-outlined",
|
||||
"ant-design_vertical-align-bottom-outlined",
|
||||
"ant-design_vertical-align-middle-outlined",
|
||||
"ant-design_vertical-align-top-outlined",
|
||||
"ant-design_vertical-left-outlined",
|
||||
"ant-design_vertical-right-outlined",
|
||||
"ant-design_video-camera-add-outlined",
|
||||
"ant-design_video-camera-outlined",
|
||||
"ant-design_wallet-outlined",
|
||||
"ant-design_warning-outlined",
|
||||
"ant-design_wechat-outlined",
|
||||
"ant-design_wechat-work-outlined",
|
||||
"ant-design_weibo-circle-outlined",
|
||||
"ant-design_weibo-outlined",
|
||||
"ant-design_weibo-square-outlined",
|
||||
"ant-design_whats-app-outlined",
|
||||
"ant-design_wifi-outlined",
|
||||
"ant-design_windows-outlined",
|
||||
"ant-design_woman-outlined",
|
||||
"ant-design_x-outlined",
|
||||
"ant-design_yahoo-outlined",
|
||||
"ant-design_youtube-outlined",
|
||||
"ant-design_yuque-outlined",
|
||||
"ant-design_zhihu-outlined",
|
||||
"ant-design_zoom-in-outlined",
|
||||
"ant-design_zoom-out-outlined"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "实底风格",
|
||||
key: "filled",
|
||||
item: [
|
||||
"ant-design_account-book-filled",
|
||||
"ant-design_alert-filled",
|
||||
"ant-design_alipay-circle-filled",
|
||||
"ant-design_alipay-square-filled",
|
||||
"ant-design_aliwangwang-filled",
|
||||
"ant-design_amazon-circle-filled",
|
||||
"ant-design_amazon-square-filled",
|
||||
"ant-design_android-filled",
|
||||
"ant-design_api-filled",
|
||||
"ant-design_apple-filled",
|
||||
"ant-design_appstore-filled",
|
||||
"ant-design_audio-filled",
|
||||
"ant-design_backward-filled",
|
||||
"ant-design_bank-filled",
|
||||
"ant-design_behance-circle-filled",
|
||||
"ant-design_behance-square-filled",
|
||||
"ant-design_bell-filled",
|
||||
"ant-design_bilibili-filled",
|
||||
"ant-design_book-filled",
|
||||
"ant-design_box-plot-filled",
|
||||
"ant-design_bug-filled",
|
||||
"ant-design_build-filled",
|
||||
"ant-design_bulb-filled",
|
||||
"ant-design_calculator-filled",
|
||||
"ant-design_calendar-filled",
|
||||
"ant-design_camera-filled",
|
||||
"ant-design_car-filled",
|
||||
"ant-design_caret-down-filled",
|
||||
"ant-design_caret-left-filled",
|
||||
"ant-design_caret-right-filled",
|
||||
"ant-design_caret-up-filled",
|
||||
"ant-design_carry-out-filled",
|
||||
"ant-design_check-circle-filled",
|
||||
"ant-design_check-square-filled",
|
||||
"ant-design_chrome-filled",
|
||||
"ant-design_ci-circle-filled",
|
||||
"ant-design_clock-circle-filled",
|
||||
"ant-design_close-circle-filled",
|
||||
"ant-design_close-square-filled",
|
||||
"ant-design_cloud-filled",
|
||||
"ant-design_code-filled",
|
||||
"ant-design_code-sandbox-circle-filled",
|
||||
"ant-design_code-sandbox-square-filled",
|
||||
"ant-design_codepen-circle-filled",
|
||||
"ant-design_codepen-square-filled",
|
||||
"ant-design_compass-filled",
|
||||
"ant-design_contacts-filled",
|
||||
"ant-design_container-filled",
|
||||
"ant-design_control-filled",
|
||||
"ant-design_copy-filled",
|
||||
"ant-design_copyright-circle-filled",
|
||||
"ant-design_credit-card-filled",
|
||||
"ant-design_crown-filled",
|
||||
"ant-design_customer-service-filled",
|
||||
"ant-design_dashboard-filled",
|
||||
"ant-design_database-filled",
|
||||
"ant-design_delete-filled",
|
||||
"ant-design_diff-filled",
|
||||
"ant-design_dingtalk-circle-filled",
|
||||
"ant-design_dingtalk-square-filled",
|
||||
"ant-design_discord-filled",
|
||||
"ant-design_dislike-filled",
|
||||
"ant-design_dollar-circle-filled",
|
||||
"ant-design_down-circle-filled",
|
||||
"ant-design_down-square-filled",
|
||||
"ant-design_dribbble-circle-filled",
|
||||
"ant-design_dribbble-square-filled",
|
||||
"ant-design_dropbox-circle-filled",
|
||||
"ant-design_dropbox-square-filled",
|
||||
"ant-design_edit-filled",
|
||||
"ant-design_environment-filled",
|
||||
"ant-design_euro-circle-filled",
|
||||
"ant-design_exclamation-circle-filled",
|
||||
"ant-design_experiment-filled",
|
||||
"ant-design_eye-filled",
|
||||
"ant-design_eye-invisible-filled",
|
||||
"ant-design_facebook-filled",
|
||||
"ant-design_fast-backward-filled",
|
||||
"ant-design_fast-forward-filled",
|
||||
"ant-design_file-add-filled",
|
||||
"ant-design_file-excel-filled",
|
||||
"ant-design_file-exclamation-filled",
|
||||
"ant-design_file-filled",
|
||||
"ant-design_file-image-filled",
|
||||
"ant-design_file-markdown-filled",
|
||||
"ant-design_file-pdf-filled",
|
||||
"ant-design_file-ppt-filled",
|
||||
"ant-design_file-text-filled",
|
||||
"ant-design_file-unknown-filled",
|
||||
"ant-design_file-word-filled",
|
||||
"ant-design_file-zip-filled",
|
||||
"ant-design_filter-filled",
|
||||
"ant-design_fire-filled",
|
||||
"ant-design_flag-filled",
|
||||
"ant-design_folder-add-filled",
|
||||
"ant-design_folder-filled",
|
||||
"ant-design_folder-open-filled",
|
||||
"ant-design_format-painter-filled",
|
||||
"ant-design_forward-filled",
|
||||
"ant-design_frown-filled",
|
||||
"ant-design_fund-filled",
|
||||
"ant-design_funnel-plot-filled",
|
||||
"ant-design_gift-filled",
|
||||
"ant-design_github-filled",
|
||||
"ant-design_gitlab-filled",
|
||||
"ant-design_gold-filled",
|
||||
"ant-design_golden-filled",
|
||||
"ant-design_google-circle-filled",
|
||||
"ant-design_google-plus-circle-filled",
|
||||
"ant-design_google-plus-square-filled",
|
||||
"ant-design_google-square-filled",
|
||||
"ant-design_hdd-filled",
|
||||
"ant-design_heart-filled",
|
||||
"ant-design_highlight-filled",
|
||||
"ant-design_home-filled",
|
||||
"ant-design_hourglass-filled",
|
||||
"ant-design_html5-filled",
|
||||
"ant-design_idcard-filled",
|
||||
"ant-design_ie-circle-filled",
|
||||
"ant-design_ie-square-filled",
|
||||
"ant-design_info-circle-filled",
|
||||
"ant-design_instagram-filled",
|
||||
"ant-design_insurance-filled",
|
||||
"ant-design_interaction-filled",
|
||||
"ant-design_layout-filled",
|
||||
"ant-design_left-circle-filled",
|
||||
"ant-design_left-square-filled",
|
||||
"ant-design_like-filled",
|
||||
"ant-design_linkedin-filled",
|
||||
"ant-design_lock-filled",
|
||||
"ant-design_mac-command-filled",
|
||||
"ant-design_mail-filled",
|
||||
"ant-design_medicine-box-filled",
|
||||
"ant-design_medium-circle-filled",
|
||||
"ant-design_medium-square-filled",
|
||||
"ant-design_meh-filled",
|
||||
"ant-design_merge-filled",
|
||||
"ant-design_message-filled",
|
||||
"ant-design_minus-circle-filled",
|
||||
"ant-design_minus-square-filled",
|
||||
"ant-design_mobile-filled",
|
||||
"ant-design_money-collect-filled",
|
||||
"ant-design_moon-filled",
|
||||
"ant-design_muted-filled",
|
||||
"ant-design_notification-filled",
|
||||
"ant-design_open-a-i-filled",
|
||||
"ant-design_pause-circle-filled",
|
||||
"ant-design_pay-circle-filled",
|
||||
"ant-design_phone-filled",
|
||||
"ant-design_picture-filled",
|
||||
"ant-design_pie-chart-filled",
|
||||
"ant-design_pinterest-filled",
|
||||
"ant-design_play-circle-filled",
|
||||
"ant-design_play-square-filled",
|
||||
"ant-design_plus-circle-filled",
|
||||
"ant-design_plus-square-filled",
|
||||
"ant-design_pound-circle-filled",
|
||||
"ant-design_printer-filled",
|
||||
"ant-design_product-filled",
|
||||
"ant-design_profile-filled",
|
||||
"ant-design_project-filled",
|
||||
"ant-design_property-safety-filled",
|
||||
"ant-design_pushpin-filled",
|
||||
"ant-design_qq-circle-filled",
|
||||
"ant-design_qq-square-filled",
|
||||
"ant-design_question-circle-filled",
|
||||
"ant-design_read-filled",
|
||||
"ant-design_reconciliation-filled",
|
||||
"ant-design_red-envelope-filled",
|
||||
"ant-design_reddit-circle-filled",
|
||||
"ant-design_reddit-square-filled",
|
||||
"ant-design_rest-filled",
|
||||
"ant-design_right-circle-filled",
|
||||
"ant-design_right-square-filled",
|
||||
"ant-design_robot-filled",
|
||||
"ant-design_rocket-filled",
|
||||
"ant-design_safety-certificate-filled",
|
||||
"ant-design_save-filled",
|
||||
"ant-design_schedule-filled",
|
||||
"ant-design_security-scan-filled",
|
||||
"ant-design_setting-filled",
|
||||
"ant-design_shop-filled",
|
||||
"ant-design_shopping-filled",
|
||||
"ant-design_signal-filled",
|
||||
"ant-design_signature-filled",
|
||||
"ant-design_sketch-circle-filled",
|
||||
"ant-design_sketch-square-filled",
|
||||
"ant-design_skin-filled",
|
||||
"ant-design_skype-filled",
|
||||
"ant-design_slack-circle-filled",
|
||||
"ant-design_slack-square-filled",
|
||||
"ant-design_sliders-filled",
|
||||
"ant-design_smile-filled",
|
||||
"ant-design_snippets-filled",
|
||||
"ant-design_sound-filled",
|
||||
"ant-design_spotify-filled",
|
||||
"ant-design_star-filled",
|
||||
"ant-design_step-backward-filled",
|
||||
"ant-design_step-forward-filled",
|
||||
"ant-design_stop-filled",
|
||||
"ant-design_sun-filled",
|
||||
"ant-design_switcher-filled",
|
||||
"ant-design_tablet-filled",
|
||||
"ant-design_tag-filled",
|
||||
"ant-design_tags-filled",
|
||||
"ant-design_taobao-circle-filled",
|
||||
"ant-design_taobao-square-filled",
|
||||
"ant-design_thunderbolt-filled",
|
||||
"ant-design_tik-tok-filled",
|
||||
"ant-design_tool-filled",
|
||||
"ant-design_trademark-circle-filled",
|
||||
"ant-design_trophy-filled",
|
||||
"ant-design_truck-filled",
|
||||
"ant-design_twitter-circle-filled",
|
||||
"ant-design_twitter-square-filled",
|
||||
"ant-design_unlock-filled",
|
||||
"ant-design_up-circle-filled",
|
||||
"ant-design_up-square-filled",
|
||||
"ant-design_usb-filled",
|
||||
"ant-design_video-camera-filled",
|
||||
"ant-design_wallet-filled",
|
||||
"ant-design_warning-filled",
|
||||
"ant-design_wechat-filled",
|
||||
"ant-design_wechat-work-filled",
|
||||
"ant-design_weibo-circle-filled",
|
||||
"ant-design_weibo-square-filled",
|
||||
"ant-design_windows-filled",
|
||||
"ant-design_x-filled",
|
||||
"ant-design_yahoo-filled",
|
||||
"ant-design_youtube-filled",
|
||||
"ant-design_yuque-filled",
|
||||
"ant-design_zhihu-circle-filled",
|
||||
"ant-design_zhihu-square-filled"
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "双色风格",
|
||||
key: "twotone",
|
||||
item: [
|
||||
"ant-design_account-book-twotone",
|
||||
"ant-design_alert-twotone",
|
||||
"ant-design_api-twotone",
|
||||
"ant-design_appstore-twotone",
|
||||
"ant-design_audio-twotone",
|
||||
"ant-design_bank-twotone",
|
||||
"ant-design_bell-twotone",
|
||||
"ant-design_book-twotone",
|
||||
"ant-design_box-plot-twotone",
|
||||
"ant-design_bug-twotone",
|
||||
"ant-design_build-twotone",
|
||||
"ant-design_bulb-twotone",
|
||||
"ant-design_calculator-twotone",
|
||||
"ant-design_calendar-twotone",
|
||||
"ant-design_camera-twotone",
|
||||
"ant-design_car-twotone",
|
||||
"ant-design_carry-out-twotone",
|
||||
"ant-design_check-circle-twotone",
|
||||
"ant-design_check-square-twotone",
|
||||
"ant-design_ci-circle-twotone",
|
||||
"ant-design_ci-twotone",
|
||||
"ant-design_clock-circle-twotone",
|
||||
"ant-design_close-circle-twotone",
|
||||
"ant-design_close-square-twotone",
|
||||
"ant-design_cloud-twotone",
|
||||
"ant-design_code-twotone",
|
||||
"ant-design_compass-twotone",
|
||||
"ant-design_contacts-twotone",
|
||||
"ant-design_container-twotone",
|
||||
"ant-design_control-twotone",
|
||||
"ant-design_copy-twotone",
|
||||
"ant-design_copyright-circle-twotone",
|
||||
"ant-design_copyright-twotone",
|
||||
"ant-design_credit-card-twotone",
|
||||
"ant-design_crown-twotone",
|
||||
"ant-design_customer-service-twotone",
|
||||
"ant-design_dashboard-twotone",
|
||||
"ant-design_database-twotone",
|
||||
"ant-design_delete-twotone",
|
||||
"ant-design_diff-twotone",
|
||||
"ant-design_dislike-twotone",
|
||||
"ant-design_dollar-circle-twotone",
|
||||
"ant-design_dollar-twotone",
|
||||
"ant-design_down-circle-twotone",
|
||||
"ant-design_down-square-twotone",
|
||||
"ant-design_edit-twotone",
|
||||
"ant-design_environment-twotone",
|
||||
"ant-design_euro-circle-twotone",
|
||||
"ant-design_euro-twotone",
|
||||
"ant-design_exclamation-circle-twotone",
|
||||
"ant-design_experiment-twotone",
|
||||
"ant-design_eye-invisible-twotone",
|
||||
"ant-design_eye-twotone",
|
||||
"ant-design_file-add-twotone",
|
||||
"ant-design_file-excel-twotone",
|
||||
"ant-design_file-exclamation-twotone",
|
||||
"ant-design_file-image-twotone",
|
||||
"ant-design_file-markdown-twotone",
|
||||
"ant-design_file-pdf-twotone",
|
||||
"ant-design_file-ppt-twotone",
|
||||
"ant-design_file-text-twotone",
|
||||
"ant-design_file-twotone",
|
||||
"ant-design_file-unknown-twotone",
|
||||
"ant-design_file-word-twotone",
|
||||
"ant-design_file-zip-twotone",
|
||||
"ant-design_filter-twotone",
|
||||
"ant-design_fire-twotone",
|
||||
"ant-design_flag-twotone",
|
||||
"ant-design_folder-add-twotone",
|
||||
"ant-design_folder-open-twotone",
|
||||
"ant-design_folder-twotone",
|
||||
"ant-design_frown-twotone",
|
||||
"ant-design_fund-twotone",
|
||||
"ant-design_funnel-plot-twotone",
|
||||
"ant-design_gift-twotone",
|
||||
"ant-design_gold-twotone",
|
||||
"ant-design_hdd-twotone",
|
||||
"ant-design_heart-twotone",
|
||||
"ant-design_highlight-twotone",
|
||||
"ant-design_home-twotone",
|
||||
"ant-design_hourglass-twotone",
|
||||
"ant-design_html5-twotone",
|
||||
"ant-design_idcard-twotone",
|
||||
"ant-design_info-circle-twotone",
|
||||
"ant-design_insurance-twotone",
|
||||
"ant-design_interaction-twotone",
|
||||
"ant-design_layout-twotone",
|
||||
"ant-design_left-circle-twotone",
|
||||
"ant-design_left-square-twotone",
|
||||
"ant-design_like-twotone",
|
||||
"ant-design_lock-twotone",
|
||||
"ant-design_mail-twotone",
|
||||
"ant-design_medicine-box-twotone",
|
||||
"ant-design_meh-twotone",
|
||||
"ant-design_message-twotone",
|
||||
"ant-design_minus-circle-twotone",
|
||||
"ant-design_minus-square-twotone",
|
||||
"ant-design_mobile-twotone",
|
||||
"ant-design_money-collect-twotone",
|
||||
"ant-design_notification-twotone",
|
||||
"ant-design_pause-circle-twotone",
|
||||
"ant-design_phone-twotone",
|
||||
"ant-design_picture-twotone",
|
||||
"ant-design_pie-chart-twotone",
|
||||
"ant-design_play-circle-twotone",
|
||||
"ant-design_play-square-twotone",
|
||||
"ant-design_plus-circle-twotone",
|
||||
"ant-design_plus-square-twotone",
|
||||
"ant-design_pound-circle-twotone",
|
||||
"ant-design_printer-twotone",
|
||||
"ant-design_profile-twotone",
|
||||
"ant-design_project-twotone",
|
||||
"ant-design_property-safety-twotone",
|
||||
"ant-design_pushpin-twotone",
|
||||
"ant-design_question-circle-twotone",
|
||||
"ant-design_reconciliation-twotone",
|
||||
"ant-design_red-envelope-twotone",
|
||||
"ant-design_rest-twotone",
|
||||
"ant-design_right-circle-twotone",
|
||||
"ant-design_right-square-twotone",
|
||||
"ant-design_rocket-twotone",
|
||||
"ant-design_safety-certificate-twotone",
|
||||
"ant-design_save-twotone",
|
||||
"ant-design_schedule-twotone",
|
||||
"ant-design_security-scan-twotone",
|
||||
"ant-design_setting-twotone",
|
||||
"ant-design_shop-twotone",
|
||||
"ant-design_shopping-twotone",
|
||||
"ant-design_skin-twotone",
|
||||
"ant-design_sliders-twotone",
|
||||
"ant-design_smile-twotone",
|
||||
"ant-design_snippets-twotone",
|
||||
"ant-design_sound-twotone",
|
||||
"ant-design_star-twotone",
|
||||
"ant-design_stop-twotone",
|
||||
"ant-design_switcher-twotone",
|
||||
"ant-design_tablet-twotone",
|
||||
"ant-design_tag-twotone",
|
||||
"ant-design_tags-twotone",
|
||||
"ant-design_thunderbolt-twotone",
|
||||
"ant-design_tool-twotone",
|
||||
"ant-design_trademark-circle-twotone",
|
||||
"ant-design_trophy-twotone",
|
||||
"ant-design_unlock-twotone",
|
||||
"ant-design_up-circle-twotone",
|
||||
"ant-design_up-square-twotone",
|
||||
"ant-design_usb-twotone",
|
||||
"ant-design_video-camera-twotone",
|
||||
"ant-design_wallet-twotone",
|
||||
"ant-design_warning-twotone"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "扩展",
|
||||
key: "extend",
|
||||
iconItem: [
|
||||
{
|
||||
name: "常用",
|
||||
key: "default",
|
||||
item: []
|
||||
},
|
||||
{
|
||||
name: "其他",
|
||||
key: "other",
|
||||
item: ["alipay"]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<el-dialog title="图标选择" :visible.sync="visible" width="800px" :close-on-click-modal="false" :modal="false">
|
||||
<el-tabs v-model="activeKey" tab-position="left" size="small" @tab-click="activeChange">
|
||||
<el-tab-pane v-for="item in iconData" :key="item.key" :label="item.name" :name="item.key" class="pl25">
|
||||
<div v-if="item.iconItem.length > 1" class="xn-icon-select-radio">
|
||||
<el-radio-group v-model="iconItemDefault" @change="radioGroupChange" size="small">
|
||||
<el-radio v-for="iconItem in item.iconItem" :key="iconItem.key" :label="iconItem.key" border>
|
||||
{{ iconItem.name }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div :key="iconItemIns.key" v-for="iconItemIns in item.iconItem">
|
||||
<div v-show="iconItemIns.key === iconItemDefault" class="xn-icon-select-list">
|
||||
<ul style="white-space: normal">
|
||||
<li v-for="icon in iconItemIns.item" :key="icon" :class="icon === value ? 'active' : ''" @click="selectIcon(icon)">
|
||||
<div :id="icon" @mouseover="iconInfo = icon">
|
||||
<el-image :src="'/assets/platform/img/svg/' + icon + '.svg'" alt="" class="xn-icons">
|
||||
<div slot="error" class="image-slot">
|
||||
<i class="el-icon-picture-outline"></i>
|
||||
</div>
|
||||
</el-image>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
iconData: [],
|
||||
activeKey: "default",
|
||||
iconItemDefault: "default",
|
||||
iconInfo: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
activeChange(val) {},
|
||||
|
||||
radioGroupChange(e) {
|
||||
console.log(e)
|
||||
},
|
||||
|
||||
//打开
|
||||
showIconModal() {
|
||||
this.visible = true
|
||||
},
|
||||
|
||||
defaultSetting(value) {
|
||||
if (value) {
|
||||
this.value = value
|
||||
// 判断展开哪个
|
||||
if (value.indexOf("-outlined") > -1 || value.indexOf("-filled") > -1 || value.indexOf("-two-tone") > -1) {
|
||||
this.activeKey = "default"
|
||||
if (value.indexOf("-two-tone") > -1) {
|
||||
this.iconItemDefault = "twotone"
|
||||
} else if (value.indexOf("-filled") > -1) {
|
||||
this.iconItemDefault = "filled"
|
||||
}
|
||||
} else if (value.indexOf("-extend") > -1) {
|
||||
// 扩展列表
|
||||
this.activeKey = "extend"
|
||||
// 如扩展其他顶部单选的情况,默认选中在这里配置,同时这里需要做判断
|
||||
// this.iconItemDefault = '您的json中配置的'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 选择图标后关闭并返回
|
||||
selectIcon(icon) {
|
||||
this.visible = false
|
||||
this.$emit("input", icon)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.iconData.push(...window.iconSelectConfig.icons)
|
||||
},
|
||||
created() {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.xn-icon-select-radio {
|
||||
padding-left: 5px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.xn-icons {
|
||||
/*font-size: 26px;*/
|
||||
/*width: 100%;*/
|
||||
/*height: 100%;*/
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.xn-icon-select-list {
|
||||
height: 360px;
|
||||
overflow: auto;
|
||||
}
|
||||
.xn-icon-select-list ul li {
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
padding: 18px;
|
||||
margin: 5px;
|
||||
border-radius: 2px;
|
||||
vertical-align: top;
|
||||
box-shadow: 0 0 0 1px rgba(5, 5, 5, 0.06);
|
||||
transition: all 0.1s;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.xn-icon-select-list ul li:hover,
|
||||
.xn-icon-select-list ul li.active {
|
||||
cursor: pointer;
|
||||
color: #ffffff;
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<div style="padding: 20px 50px">
|
||||
<el-timeline>
|
||||
<el-timeline-item timestamp="下载模板" placement="top">
|
||||
<el-card>
|
||||
<el-button size="medium" style="width: 200px" @click="$downLoad(temp_url)" icon="el-icon-download">下载模板</el-button>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item timestamp="上传文件" placement="top">
|
||||
<el-card>
|
||||
<el-form>
|
||||
<el-form-item label="请选择更新模式" v-if="is_show_radio">
|
||||
<el-radio-group v-model="importData.isFlag">
|
||||
<el-radio-button label="true">清空更新</el-radio-button>
|
||||
<el-radio-button label="false">追加</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-upload
|
||||
name="file"
|
||||
ref="upload"
|
||||
:on-remove="
|
||||
(file, fileList) => {
|
||||
importData.fileList = fileHandleRemove(file, fileList)
|
||||
importResult = {
|
||||
errorCount: 0,
|
||||
successCount: 0,
|
||||
totalCount: 0,
|
||||
errorList: []
|
||||
}
|
||||
}
|
||||
"
|
||||
:on-change="
|
||||
(file, fileList) => {
|
||||
importData.fileList = fileHandleChange(file, fileList, { type: ['xls', 'xlsx'] })
|
||||
}
|
||||
"
|
||||
:auto-upload="false"
|
||||
action
|
||||
:limit="1"
|
||||
:file-list="importData.fileList"
|
||||
>
|
||||
<el-button size="medium" type="" icon="el-icon-upload" style="width: 200px">选择文件</el-button>
|
||||
<div class="el-upload__tip" slot="tip" style="color: #f56c6c">只能上传 xls/xlsx 文件</div>
|
||||
</el-upload>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
<el-timeline-item placement="top" timestamp="导入结果">
|
||||
<el-card shadow="never">
|
||||
<p>总记录数:{{ errorInfoData.totalCount }}</p>
|
||||
<p>
|
||||
成功数:
|
||||
<span class="text-success">{{ errorInfoData.successCount }}</span>
|
||||
</p>
|
||||
<p>
|
||||
错误数:
|
||||
<span class="text-danger">{{ errorInfoData.errorCount }}</span>
|
||||
</p>
|
||||
<el-link @click="exportErrors" type="primary" v-if="errorInfoData.errorCount > 0">下载错误记录</el-link>
|
||||
</el-card>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
<div style="text-align: right">
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<!-- <el-button @click="importVisible = false" :disabled="importLoading">取 消</el-button>-->
|
||||
<el-button type="primary" @click="doImport">确 定</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
props: {
|
||||
temp_url: { type: String },
|
||||
post_url: { type: String },
|
||||
business_id: { type: String },
|
||||
is_show_radio: { type: Boolean, default: false }
|
||||
},
|
||||
mounted() {
|
||||
const s = document.createElement("script")
|
||||
s.type = "text/javascript"
|
||||
s.src = "/assets/platform/plugins/xlsx/xlsx.full.min.js"
|
||||
document.body.appendChild(s)
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
importData: {
|
||||
fileList: [],
|
||||
isFlag: false
|
||||
},
|
||||
errorInfoData: {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: 0
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resetImportData() {
|
||||
this.importData = {
|
||||
fileList: [],
|
||||
isFlag: false
|
||||
}
|
||||
this.errorInfoData = {
|
||||
totalCount: 0,
|
||||
successCount: 0,
|
||||
errorCount: 0,
|
||||
errorList: 0
|
||||
}
|
||||
},
|
||||
doImport() {
|
||||
if (this.importData.fileList.length === 0) {
|
||||
this.$message.error({
|
||||
title: "错误",
|
||||
message: "请选择文件!"
|
||||
})
|
||||
return
|
||||
}
|
||||
const data = new FormData()
|
||||
data.append("isFlag", this.importData.isFlag)
|
||||
data.append("businessId", this.business_id)
|
||||
this.importData.fileList.forEach((val) => {
|
||||
data.append("file", val.raw, val.raw.name)
|
||||
})
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: "正在导入中,请稍后...",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)"
|
||||
})
|
||||
this.$axios.post(this.post_url, data).then((res) => {
|
||||
loading.close()
|
||||
if (res.code === 0) {
|
||||
if (res.data) {
|
||||
this.$message.warning("导入失败")
|
||||
this.errorInfoData = res.data
|
||||
this.$emit("flush")
|
||||
} else {
|
||||
this.$message.success("导入成功")
|
||||
this.$emit("flush")
|
||||
this.resetImportData()
|
||||
}
|
||||
} else {
|
||||
this.$message.warning("导入失败")
|
||||
}
|
||||
})
|
||||
},
|
||||
exportErrors() {
|
||||
const data = this.errorInfoData.errorList
|
||||
|
||||
// 创建工作簿
|
||||
const workbook = XLSX.utils.book_new()
|
||||
|
||||
// 创建工作表
|
||||
const worksheet = XLSX.utils.json_to_sheet(data)
|
||||
|
||||
// 将工作表添加到工作簿
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Sheet1")
|
||||
|
||||
// 将工作簿转换为二进制对象
|
||||
const excelBuffer = XLSX.write(workbook, { bookType: "xlsx", type: "array" })
|
||||
|
||||
// 将二进制对象转换为Blob对象
|
||||
const blob = new Blob([excelBuffer], { type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" })
|
||||
|
||||
// 创建下载链接并设置相关属性
|
||||
const url = window.URL.createObjectURL(blob)
|
||||
const link = document.createElement("a")
|
||||
link.href = url
|
||||
link.download = "错误记录.xlsx"
|
||||
|
||||
// 模拟点击下载链接
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
// 清理下载链接
|
||||
document.body.removeChild(link)
|
||||
window.URL.revokeObjectURL(url)
|
||||
},
|
||||
fileHandleRemove(file, fileList) {
|
||||
return fileList
|
||||
},
|
||||
fileHandleChange(file, fileList, { type, size }) {
|
||||
const removeFile = () => {
|
||||
fileList.splice(fileList.findIndex((v) => v === file))
|
||||
}
|
||||
|
||||
if (!file.size) {
|
||||
this.$message.warning("您选择的是空文件!")
|
||||
removeFile()
|
||||
}
|
||||
|
||||
if (type && type.length && !type.includes(file.name.split(".").pop().toLowerCase())) {
|
||||
this.$message.warning(`文件只能是 ${type.map((v) => v.toUpperCase()).join("/")} 格式!`)
|
||||
removeFile()
|
||||
}
|
||||
|
||||
if (size && !file.size < size) {
|
||||
this.$message.warning(`文件大小不能超过 ${size / 1024 / 1024}MB!`)
|
||||
removeFile()
|
||||
}
|
||||
|
||||
return fileList
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.el-card__body {
|
||||
padding: 25px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,16 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<slot></slot>
|
||||
<div class="search-query">
|
||||
<el-button type="primary" icon="el-icon-search" @click="$emit('search', null)">搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "pageFormSearch"
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div class="search-item">
|
||||
<div class="search-item-label" v-if="label">
|
||||
{{ label }}
|
||||
</div>
|
||||
<div class="search-item-option">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "searchItem",
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,104 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 弹窗模式(默认) -->
|
||||
<template v-if="mode === 'dialog'">
|
||||
<!-- 触发按钮 -->
|
||||
<!-- <slot name="trigger">-->
|
||||
<!-- <el-button type="primary" size="small" @click="openDialog">二维码</el-button>-->
|
||||
<!-- </slot>-->
|
||||
|
||||
<!-- 弹出框 -->
|
||||
<el-dialog
|
||||
title="二维码"
|
||||
:visible.sync="dialogVisible"
|
||||
width="40%"
|
||||
append-to-body
|
||||
>
|
||||
<div class="qrcode-container" style="text-align: center;">
|
||||
<qrcode v-if="url" :value="url" :options="{ width: width }" class="signature-qrcode"></qrcode>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<!-- 内联模式 -->
|
||||
<template v-else-if="mode === 'inline'">
|
||||
<div class="qrcode-inline-container">
|
||||
<qrcode v-if="url" :value="url" :options="{ width: width }" class="signature-qrcode"></qrcode>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
|
||||
module.exports = {
|
||||
name: 'QrCodeDisplay',
|
||||
props: {
|
||||
// 二维码内容
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
// 显示模式:dialog(默认)或 inline
|
||||
mode: {
|
||||
type: String,
|
||||
default: 'dialog',
|
||||
validator: (value) => ['dialog', 'inline'].includes(value)
|
||||
},
|
||||
// 控制弹窗显示(可用于外部 v-model 控制)
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
width: {
|
||||
type: Number,
|
||||
default: 200
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
dialogVisible: this.value // 初始化为传入的 value
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
// 同步 v-model 的 value 变化到 dialogVisible
|
||||
value(newVal) {
|
||||
this.dialogVisible = newVal
|
||||
},
|
||||
// 同步 dialogVisible 状态到外部(实现 v-model)
|
||||
dialogVisible(newVal) {
|
||||
this.$emit('input', newVal)
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
openDialog() {
|
||||
this.dialogVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.signature-qrcode {
|
||||
display: inline-block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.qrcode-container {
|
||||
padding: 20px 0;
|
||||
}
|
||||
|
||||
.qrcode-inline-container {
|
||||
display: inline-block;
|
||||
padding: 10px;
|
||||
border: 1px dashed #ccc;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
value: String,
|
||||
/*最大高度,超过该高度显示折叠展开*/
|
||||
height: Number
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isExpanded: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onView(e) {
|
||||
if (e.target.tagName === "IMG") {
|
||||
vant && vant.ImagePreview([e.target.src])
|
||||
}
|
||||
},
|
||||
toggleDetails() {
|
||||
this.isExpanded = !this.isExpanded
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div class="content" v-html="value" v-if="value" @click="onView" :style="{ maxHeight: isExpanded ? 'none' : height + 'px' }"></div>
|
||||
<div v-if="value && height" class="toggle-button" @click="toggleDetails">
|
||||
{{ isExpanded ? "收起" : "查看全部详情" }}
|
||||
<van-icon :name="isExpanded ? 'arrow-up' : 'arrow-down'"></van-icon>
|
||||
</div>
|
||||
<van-empty v-if="!value" description="没有更多了"></van-empty>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.content {
|
||||
padding: 10px;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
overflow: hidden;
|
||||
word-break: break-word;
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
transition: max-height 0.3s ease;
|
||||
}
|
||||
|
||||
.content img {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.toggle-button {
|
||||
cursor: pointer;
|
||||
text-align: center;
|
||||
margin: 10px 0;
|
||||
font-size: 14px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
color: rgb(119 115 115);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<div class="sign-container">
|
||||
<template v-if="Object.keys(res).length === 0">
|
||||
<div class="reader-container">
|
||||
<div id="reader"></div>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="weui-msg">
|
||||
<div class="weui-msg__icon-area">
|
||||
<i v-if="res?.code !== 0" class="weui-icon-warn weui-icon_msg"></i>
|
||||
<i v-if="res?.code === 0" class="weui-icon-success weui-icon_msg"></i>
|
||||
</div>
|
||||
<div class="weui-msg__text-area">
|
||||
<div class="weui-msg__title">温馨提醒</div>
|
||||
<div v-html="res?.msg"></div>
|
||||
</div>
|
||||
<div class="weui-msg__opr-area">
|
||||
<p class="weui-btn-area">
|
||||
<a @click="res = {}; getCameras()"
|
||||
class="weui-btn weui-btn_primary">重新扫描</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "scanCode",
|
||||
props: {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
html5QrCode: null,
|
||||
scanStatus: true,
|
||||
cameraId: '',
|
||||
res: {},
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getCameras() {
|
||||
Html5Qrcode.getCameras()
|
||||
.then((devices) => {
|
||||
if (devices && devices.length) {
|
||||
// 如果有2个摄像头,1为前置的
|
||||
if (devices.length > 1) {
|
||||
this.cameraId = devices[1].id;
|
||||
} else {
|
||||
this.cameraId = devices[0].id;
|
||||
}
|
||||
let isHuawei = navigator.userAgent.toLowerCase().match(/huawei/i) === 'huawei';
|
||||
if(isHuawei) {
|
||||
const backCamera = devices.filter(o => o.label.includes('back'))
|
||||
this.cameraId = backCamera[0].id;
|
||||
}
|
||||
this.start();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$toast('未获取到摄像头信息')
|
||||
})
|
||||
},
|
||||
start() {
|
||||
this.html5QrCode = new Html5Qrcode("reader");
|
||||
this.html5QrCode.start(
|
||||
this.cameraId, // retreived in the previous step.
|
||||
{
|
||||
fps: 100, // sets the framerate to 10 frame per second,
|
||||
qrbox: {width: 1000, height: 1000}, // sets only 250 X 250 region of viewfinder to
|
||||
},
|
||||
async (decodedText, decodedResult) => {
|
||||
this.res = await this.$axios.post(decodedText)
|
||||
this.closeScan()
|
||||
},
|
||||
(errorMessage) => {
|
||||
console.log(errorMessage);
|
||||
}
|
||||
)
|
||||
.catch((err) => {
|
||||
alert(err)
|
||||
console.log(`Unable to start scanning, error: ` + err);
|
||||
});
|
||||
},
|
||||
closeScan() {
|
||||
this.html5QrCode?.stop()
|
||||
.then((ignore) => {
|
||||
console.log("QR Code scanning stopped.");
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log("Unable to stop scanning.");
|
||||
});
|
||||
},
|
||||
init() {
|
||||
this.getCameras()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.sign-container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.reader-container {
|
||||
padding-top: 50px;
|
||||
}
|
||||
#reader {
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
.weui-msg__icon-area {
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="h5-signature">
|
||||
<div v-if="!showSignaturePanel" class="main-panel">
|
||||
<slot v-if="$slots.default"></slot>
|
||||
<template v-else>
|
||||
<van-button @click="openSignature" class="open-button" size="middle" plain native-type="button">打开签字板</van-button>
|
||||
<van-image :src="signatureContent"></van-image>
|
||||
</template>
|
||||
</div>
|
||||
<van-popup v-model="showSignaturePanel" :style="{ height: '100vh', width: '100vw' }">
|
||||
<signature @save="save"></signature>
|
||||
</van-popup>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "h5",
|
||||
components: {
|
||||
signature: httpVueLoader("/components/plugins/sysSignature/index.vue?v=" + new Date().getTime())
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
this.signatureContent = val
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
signatureContent: null,
|
||||
showSignaturePanel: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openSignature() {
|
||||
this.showSignaturePanel = true
|
||||
},
|
||||
|
||||
save(signature) {
|
||||
const file = this.base64ToFile(signature, "signature.png")
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
axios
|
||||
.post("/platform/signature/saveH5Signature", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
.then((res) => {
|
||||
return res.data
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
alert("保存成功")
|
||||
this.showSignaturePanel = false
|
||||
this.$emit("input", res.data)
|
||||
} 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 })
|
||||
}
|
||||
},
|
||||
created() {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.h5-signature {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.main-panel {
|
||||
position: relative;
|
||||
flex-direction: column-reverse;
|
||||
display: flex;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
.main-panel .van-image {
|
||||
min-height: 150px;
|
||||
}
|
||||
|
||||
.main-panel .open-button {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,171 @@
|
||||
<template>
|
||||
<div class="signature-wrap">
|
||||
<canvas id="canvas"></canvas>
|
||||
<div :class="isLikelyMobile() ? 'action-buttons' : 'action-buttons pc-action-buttons'">
|
||||
<button @click="clear" class="action-button-danger" type="button">清空</button>
|
||||
<button @click="undo" class="action-button-warning" type="button">撤销</button>
|
||||
<button @click="save" class="action-button-primary" type="button">确定</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// function a() {
|
||||
// // 检查当前屏幕方向
|
||||
// if (window.orientation === 0 || window.orientation === 180) {
|
||||
// // 竖屏状态
|
||||
// console.log("竖屏状态")
|
||||
// alert("当前不支持竖屏,请将设备调整为横屏!")
|
||||
// } else {
|
||||
// // 横屏状态,执行禁止旋转的操作
|
||||
// alert("当前不支持横屏,请将设备调整为竖屏!")
|
||||
// // 可以在这里使内容固定在竖屏方向
|
||||
// const innerWidth = window.innerWidth
|
||||
// const innerHeight = window.innerHeight
|
||||
// alert(innerHeight)
|
||||
// alert(innerWidth)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// window.addEventListener("orientationchange", a)
|
||||
// a()
|
||||
|
||||
let signature = null
|
||||
module.exports = {
|
||||
name: "sysSignature",
|
||||
data() {
|
||||
return {
|
||||
content: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clear() {
|
||||
signature.clear()
|
||||
},
|
||||
undo() {
|
||||
signature.undo()
|
||||
},
|
||||
rotate() {
|
||||
signature.getRotateCanvas(90)
|
||||
},
|
||||
isLikelyMobile() {
|
||||
const isTouchDevice = 'ontouchstart' in window || navigator.maxTouchPoints > 0;
|
||||
const isSmallScreen = window.screen.width <= 768;
|
||||
const hasMobileUA = /Android|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
|
||||
|
||||
return hasMobileUA || (isTouchDevice && isSmallScreen);
|
||||
},
|
||||
save() {
|
||||
if (signature.isEmpty()) {
|
||||
alert("请签字后再保存")
|
||||
return
|
||||
}
|
||||
// const png = signature.getPNG()
|
||||
//旋转一下 横过来
|
||||
const rotateCanvas = signature.getRotateCanvas(-90)
|
||||
if (this.isLikelyMobile()) {
|
||||
this.$emit("save", rotateCanvas.toDataURL())
|
||||
} else {
|
||||
this.$emit("save", signature.toDataURL())
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
const innerWidth = window.innerWidth
|
||||
const innerHeight = window.innerHeight
|
||||
|
||||
const canvas = document.getElementById("canvas")
|
||||
canvas.width = window.innerWidth
|
||||
canvas.height = window.innerHeight
|
||||
signature = new SmoothSignature(canvas, {
|
||||
width: innerWidth - 30,
|
||||
height: innerHeight - 30,
|
||||
scale: 2,
|
||||
minWidth: 4,
|
||||
maxWidth: 10,
|
||||
color: "#000000"
|
||||
// bgColor: "#f6f6f6"
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.signature-wrap {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 15px;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.signature-wrap .action {
|
||||
width: 50px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.signature-wrap .action-buttons {
|
||||
white-space: nowrap;
|
||||
transform: rotate(90deg);
|
||||
display: flex;
|
||||
column-gap: 10px;
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 60px;
|
||||
transform: rotate(90deg);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
.signature-wrap .pc-action-buttons {
|
||||
bottom: 50px;
|
||||
right: 50px;
|
||||
left: unset;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
.action-buttons button {
|
||||
color: #ffffff;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
padding: 3px 12px;
|
||||
margin: 0;
|
||||
font-size: 15px;
|
||||
line-height: 1.4;
|
||||
text-align: center;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: opacity 0.15s ease;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.action-button-primary {
|
||||
background-color: #1677ff;
|
||||
border: 1px solid #1677ff;
|
||||
}
|
||||
|
||||
.action-button-danger {
|
||||
background-color: #ff3141;
|
||||
border: 1px solid #ff3141;
|
||||
}
|
||||
|
||||
.action-button-warning {
|
||||
background-color: #ff8f1f;
|
||||
border: 1px solid #ff8f1f;
|
||||
}
|
||||
|
||||
.signature-wrap canvas {
|
||||
flex: 1;
|
||||
border-radius: 10px;
|
||||
border: 2px dashed #ccc;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<el-card shadow="never" class="pc-signature-card">
|
||||
<div class="pc-signature">
|
||||
<el-link type="primary">扫描下方二维码进行签字</el-link>
|
||||
<div class="signature-body" v-loading="loading">
|
||||
<QrCode v-if="!signatureContent" :options="{ width: 126 }" :value="signatureAddress"
|
||||
class="signature-qrcode"></QrCode>
|
||||
<el-image v-if="signatureContent" :src="signatureContent" class="signature-image"></el-image>
|
||||
</div>
|
||||
<el-link type="danger" icon="el-icon-edit" @click="signatureAgain" v-if="signatureContent">重签</el-link>
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "sysSignaturePc",
|
||||
store,
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.signatureContent = val
|
||||
this.$emit("input", this.signatureContent)
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
//前端唯一标识 当然后端还有用户id作为唯一标识
|
||||
id: new Date().getTime() + Math.floor(Math.random() * 10000) + 1,
|
||||
signatureAddress: null,
|
||||
interval: null,
|
||||
signatureContent: null,
|
||||
loading: true,
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async init() {
|
||||
const {code, data} = await this.$axios.post("/platform/signature/get")
|
||||
if (code === 0) {
|
||||
this.signatureContent = data && data.signature
|
||||
this.$emit("input", this.signatureContent)
|
||||
}
|
||||
this.signatureAddress = origin + "/platform/signature/scanPcCode?id=" + this.id
|
||||
},
|
||||
signatureAgain() {
|
||||
const val = sessionStorage.getItem("h5-scan-code-signature-" + this.id)
|
||||
this.signatureContent = val
|
||||
this.$emit("input", val)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.init()
|
||||
webSocketPubSub.subscribe("h5-scan-code-signature", (data) => {
|
||||
console.info("ws:收到签字成功消息:", data)
|
||||
this.signatureContent = data.value
|
||||
this.$emit("input", data.value)
|
||||
})
|
||||
|
||||
// 可选:轮询或 watch ready 状态
|
||||
const checkReady = () => {
|
||||
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
|
||||
console.info("ws.readyState is open");
|
||||
this.loading = false;
|
||||
console.log(this.signatureAddress)
|
||||
} else {
|
||||
setTimeout(checkReady, 100);
|
||||
}
|
||||
};
|
||||
checkReady();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.pc-signature-card {
|
||||
border: 1px solid var(--border-color-base);
|
||||
}
|
||||
|
||||
.pc-signature {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
row-gap: 20px;
|
||||
}
|
||||
|
||||
.signature-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.signature-qrcode {
|
||||
transition: opacity 0.5s ease;
|
||||
}
|
||||
|
||||
.signature-image {
|
||||
height: 100px;
|
||||
width: auto;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div ref="svgContainer" class="svg-icon" :style="{ 'font-size': size + 'px' }"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
name: {
|
||||
type: String
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 26
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
methods: {},
|
||||
created() {
|
||||
if (this.name) {
|
||||
fetch("/assets/platform/img/svg/" + this.name + ".svg")
|
||||
.then(async (res) => {
|
||||
const svg = await res.text()
|
||||
if (this.$refs.svgContainer) {
|
||||
this.$refs.svgContainer.innerHTML = ""
|
||||
this.$refs.svgContainer.innerHTML = svg
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.svg-icon {
|
||||
display: inline-flex;
|
||||
font-size: 20px;
|
||||
}
|
||||
.svg-icon svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,522 @@
|
||||
<template>
|
||||
<div class="ele-table-tool">
|
||||
<div class="ele-table-tool-title">
|
||||
<div class="ele-table-tool-title-label">
|
||||
<slot v-if="$slots.label" name="label"></slot>
|
||||
<span v-else>{{ label }}</span>
|
||||
</div>
|
||||
<div class="ele-table-tool-title-content">
|
||||
<slot v-if="$slots.content" name="content"></slot>
|
||||
<span v-else>{{ content }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ele-tool">
|
||||
<div class="ele-space">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="ele-tool-item ele-action" v-if="columns && columns.length > 0">
|
||||
<!--控制列-->
|
||||
<i class="el-icon-menu" @click="openColumns" ></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="列设置" :visible.sync="columnDialog" width="40%">
|
||||
<div class="column-setting-container">
|
||||
<div class="column-setting-header">
|
||||
<el-checkbox
|
||||
:indeterminate="isIndeterminate"
|
||||
v-model="checkAll"
|
||||
@change="handleCheckAllChange">
|
||||
全选
|
||||
</el-checkbox>
|
||||
<span class="column-count">已选择 {{ checkedColumns.length }} / {{ tempColumns.length }} 列</span>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
:data="tempColumns"
|
||||
ref="columnTableRef"
|
||||
@selection-change="handleSelectionChange"
|
||||
row-key="prop"
|
||||
class="column-setting-table"
|
||||
max-height="500px">
|
||||
<el-table-column type="selection" width="55" :selectable="isColumnSelectable"></el-table-column>
|
||||
<el-table-column label="标题" prop="label" min-width="120"></el-table-column>
|
||||
<!-- <el-table-column label="字段" prop="prop" min-width="100"></el-table-column>-->
|
||||
<el-table-column label="宽度PX" prop="width" min-width="120">
|
||||
<template slot-scope="{row}">
|
||||
<el-input
|
||||
v-model="row.width"
|
||||
type="number"
|
||||
size="mini"
|
||||
placeholder="自动">
|
||||
</el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="固定" prop="fixed" min-width="120">
|
||||
<template slot-scope="{row}">
|
||||
<el-radio-group v-model="row.fixed" size="mini" boder>
|
||||
<el-radio-button label="left">左侧</el-radio-button>
|
||||
<el-radio-button label="">自动</el-radio-button>
|
||||
<el-radio-button label="right">右侧</el-radio-button>
|
||||
</el-radio-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="columnDialog = false">取 消</el-button>
|
||||
<el-button type="primary" @click="doColumns">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
label: {
|
||||
type: String,
|
||||
default: "表格数据"
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: function () {
|
||||
return []
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
columnDialog: false,
|
||||
checkedColumns: [],
|
||||
checkAll: false,
|
||||
isIndeterminate: false,
|
||||
sortable: null, // Sortable实例
|
||||
tempColumns: [] // 临时存储列配置,避免直接修改props
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 打开列设置
|
||||
openColumns() {
|
||||
// 创建临时列配置的深拷贝,避免直接修改props
|
||||
this.tempColumns = JSON.parse(JSON.stringify(this.columns))
|
||||
this.columnDialog = true
|
||||
this.initColumnSelection()
|
||||
this.initSortable()
|
||||
},
|
||||
|
||||
// 初始化拖拽排序
|
||||
initSortable() {
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.columnTableRef && this.$refs.columnTableRef.$el) {
|
||||
const tbody = this.$refs.columnTableRef.$el.querySelector('.el-table__body-wrapper tbody')
|
||||
if (tbody && !this.sortable) {
|
||||
this.sortable = new Sortable(tbody, {
|
||||
animation: 150,
|
||||
ghostClass: 'sortable-ghost',
|
||||
chosenClass: 'sortable-chosen',
|
||||
dragClass: 'sortable-drag',
|
||||
onEnd: (evt) => {
|
||||
const {oldIndex, newIndex} = evt
|
||||
if (oldIndex !== newIndex) {
|
||||
// 在tempColumns中重新排序
|
||||
const movedItem = this.tempColumns.splice(oldIndex, 1)[0]
|
||||
this.tempColumns.splice(newIndex, 0, movedItem)
|
||||
|
||||
// 重新初始化选择状态
|
||||
this.$nextTick(() => {
|
||||
this.initColumnSelection()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 初始化列选择状态
|
||||
initColumnSelection() {
|
||||
// 设置默认选中状态(默认全部显示,除非明确指定visible为false)
|
||||
this.checkedColumns = this.tempColumns.filter(col => col.visible !== false)
|
||||
|
||||
// 更新全选状态
|
||||
this.updateCheckAllStatus()
|
||||
|
||||
// 设置表格选中状态
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.columnTableRef) {
|
||||
this.tempColumns.forEach(row => {
|
||||
if (row.visible !== false) {
|
||||
this.$refs.columnTableRef.toggleRowSelection(row, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
// 处理列选择变化
|
||||
handleSelectionChange(selection) {
|
||||
this.checkedColumns = selection
|
||||
this.updateCheckAllStatus()
|
||||
},
|
||||
|
||||
// 更新全选状态
|
||||
updateCheckAllStatus() {
|
||||
const selectableColumns = this.tempColumns.filter(col => this.isColumnSelectable(col))
|
||||
this.checkAll = this.checkedColumns.length === selectableColumns.length
|
||||
this.isIndeterminate = this.checkedColumns.length > 0 && this.checkedColumns.length < selectableColumns.length
|
||||
},
|
||||
|
||||
// 处理全选变化
|
||||
handleCheckAllChange(val) {
|
||||
const selectableColumns = this.tempColumns.filter(col => this.isColumnSelectable(col))
|
||||
|
||||
if (val) {
|
||||
this.checkedColumns = [...selectableColumns]
|
||||
selectableColumns.forEach(row => {
|
||||
this.$refs.columnTableRef.toggleRowSelection(row, true)
|
||||
})
|
||||
} else {
|
||||
this.checkedColumns = []
|
||||
this.$refs.columnTableRef.clearSelection()
|
||||
}
|
||||
this.isIndeterminate = false
|
||||
},
|
||||
|
||||
// 判断列是否可选择
|
||||
isColumnSelectable(row) {
|
||||
return row.required !== true && row.type !== 'selection' && row.type !== 'index'
|
||||
},
|
||||
|
||||
// 确定保存列设置
|
||||
doColumns() {
|
||||
// 基于tempColumns创建新的列配置数组,保留用户修改的宽度和固定值
|
||||
const updatedColumns = this.tempColumns.map(col => {
|
||||
return {
|
||||
...col,
|
||||
visible: this.checkedColumns.map(cc => cc.prop).includes(col.prop)
|
||||
}
|
||||
})
|
||||
|
||||
this.$emit('update:columns', updatedColumns)
|
||||
this.columnDialog = false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
columns: {
|
||||
handler() {
|
||||
// 只有在对话框未打开时才初始化,避免覆盖用户正在编辑的tempColumns
|
||||
if (!this.columnDialog) {
|
||||
this.initColumnSelection()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
|
||||
columnDialog(val) {
|
||||
if (!val && this.sortable) {
|
||||
this.sortable.destroy()
|
||||
this.sortable = null
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.sortable) {
|
||||
this.sortable.destroy()
|
||||
this.sortable = null
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ele-table-tool:not(.ele-table-tool-default):not(.ele-table-tools-none) .ele-tool .ele-tool-item {
|
||||
padding: 6px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
border: unset;
|
||||
border-radius: unset;
|
||||
font-size: 14px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-table-tool-title {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.ele-table-tool-title-label {
|
||||
position: relative;
|
||||
padding-left: 1em;
|
||||
color: var(--color-primary);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ele-table-tool-title-label::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 5px;
|
||||
height: 1.3em;
|
||||
background-color: var(--color-primary);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.ele-table-tool {
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
-ms-flex-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.ele-table-tool,
|
||||
.ele-table-tool .ele-table-tool-title {
|
||||
display: -webkit-box;
|
||||
display: -ms-flexbox;
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-table-tool-title {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: auto;
|
||||
flex: auto;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
-ms-flex-align: center;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-table-tool-title > .ele-table-tool-title-label {
|
||||
margin-right: 8px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-table-tool-title > .ele-table-tool-title-content {
|
||||
-webkit-box-flex: 1;
|
||||
-ms-flex: 1;
|
||||
flex: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-tool {
|
||||
margin: 5px 0 5px auto;
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-ms-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-tool .ele-tool-item {
|
||||
font-size: 18px;
|
||||
padding: 0 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-tool .ele-tool-item .el-dropdown > i {
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.ele-table-tool .ele-tool .ele-tool-item + .ele-tool-item {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.ele-table-tool.ele-toolbar-form .ele-table-tool-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ele-table-tool.ele-toolbar-form .ele-table-tool-title .el-form-item,
|
||||
.ele-table-tool.ele-toolbar-form .ele-table-tool-title .ele-form-actions {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.ele-table-tool.ele-toolbar-actions .ele-table-tool-title {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.ele-table-tool.ele-toolbar-actions .ele-table-tool-title > .el-button,
|
||||
.ele-table-tool.ele-toolbar-actions .ele-table-tool-title > .el-dropdown,
|
||||
.ele-table-tool.ele-toolbar-actions .ele-table-tool-title > .el-link,
|
||||
.ele-table-tool.ele-toolbar-actions .ele-table-tool-title > .el-tag,
|
||||
.ele-table-tool.ele-toolbar-actions .ele-table-tool-title > .ele-action {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.ele-table-tool:not(.ele-table-tool-default):not(.ele-table-tools-none) .ele-tool .ele-tool-item {
|
||||
padding: 6px;
|
||||
line-height: 1;
|
||||
text-align: center;
|
||||
border: 1px solid var(--border-color-base);
|
||||
border-radius: 50%;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ele-table-tool:not(.ele-table-tool-default):not(.ele-table-tools-none) .ele-tool .ele-tool-item .el-dropdown > i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.ele-table-tool:not(.ele-table-tool-default):not(.ele-table-tools-none) .ele-tool .ele-tool-item:hover {
|
||||
color: var(--color-primary);
|
||||
border-color: var(--color-primary-3);
|
||||
background-color: var(--color-primary-1);
|
||||
}
|
||||
|
||||
.ele-table-tool:not(.ele-table-tool-default):not(.ele-table-tools-none) .ele-tool .ele-tool-item:hover .el-dropdown > i {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.ele-table-tool-default {
|
||||
margin-bottom: 0;
|
||||
padding: 5px 15px;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
background: var(--table-header-background-color);
|
||||
border-top: 1px solid var(--border-color-lighter);
|
||||
border-left: 1px solid var(--border-color-lighter);
|
||||
border-right: 1px solid var(--border-color-lighter);
|
||||
}
|
||||
|
||||
.ele-table-tool-default .ele-tool .ele-tool-item {
|
||||
font-size: 16px;
|
||||
padding: 5px 6px;
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--border-color-light);
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.ele-table-tool-default .ele-tool .ele-tool-item .el-dropdown > i {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.ele-tab-tool {
|
||||
padding: 0 15px;
|
||||
-ms-flex-negative: 0;
|
||||
flex-shrink: 0;
|
||||
width: auto;
|
||||
min-width: 40px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
-webkit-transition: background-color 0.3s,
|
||||
color 0.3s;
|
||||
transition: background-color 0.3s,
|
||||
color 0.3s;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ele-tab-tool .el-icon-house {
|
||||
font-size: 16px;
|
||||
vertical-align: -1px;
|
||||
}
|
||||
|
||||
.ele-tab-tool.is-tab:hover {
|
||||
color: var(--color-primary);
|
||||
background: var(--header-tool-hover-bg);
|
||||
}
|
||||
|
||||
.ele-tab-tool.is-tab:after {
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background: var(--color-primary);
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.ele-tab-tool.is-tab.is-active {
|
||||
color: var(--color-primary);
|
||||
background: var(--color-primary-1);
|
||||
}
|
||||
|
||||
.ele-tab-tool.is-tab.is-active:after {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.column-setting-container {
|
||||
/*max-height: calc(100vh - 182px);
|
||||
overflow-y: auto;*/
|
||||
}
|
||||
|
||||
.column-setting-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding: 10px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.column-count {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.column-setting-table {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* 拖拽排序样式 */
|
||||
.sortable-ghost {
|
||||
opacity: 0.5;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.sortable-chosen {
|
||||
background-color: #e6f7ff;
|
||||
border: 1px dashed #1890ff;
|
||||
}
|
||||
|
||||
.sortable-drag {
|
||||
opacity: 0.8;
|
||||
transform: rotate(5deg);
|
||||
}
|
||||
|
||||
/* 表格行拖拽提示 */
|
||||
.column-setting-table .el-table__row {
|
||||
cursor: move;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.column-setting-table .el-table__row:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
/* 拖拽提示文本 */
|
||||
.column-setting-container::before {
|
||||
content: "💡 提示:可以拖拽表格行来调整列的显示顺序";
|
||||
display: block;
|
||||
margin-bottom: 10px;
|
||||
padding: 8px;
|
||||
background-color: #fafafa;
|
||||
border-left: 3px solid var(--color-primary);
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,287 @@
|
||||
<template>
|
||||
<div>
|
||||
<div ref="editorRef"></div>
|
||||
<input type="file" style="display: none" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const { $, BtnMenu, Panel, Tooltip } = wangEditor
|
||||
|
||||
class PDFMenu extends BtnMenu {
|
||||
constructor(editor) {
|
||||
const $elem = wangEditor.$(
|
||||
`<div class="w-e-menu" data-title="上传pdf">
|
||||
<i class="fa fa-file-pdf-o" aria-hidden="true" /></i>
|
||||
</div>`
|
||||
)
|
||||
super($elem, editor)
|
||||
}
|
||||
|
||||
clickHandler() {
|
||||
const _this = this
|
||||
const inputFileElement = document.querySelector('#'+this.editor.textElemId).parentElement.parentElement.nextElementSibling
|
||||
inputFileElement.click()
|
||||
inputFileElement.addEventListener('change', function () {
|
||||
const file = this.files[0];
|
||||
const fileName = file.name
|
||||
|
||||
if (!fileName.toLowerCase().endsWith('.pdf')) {
|
||||
alert('请上传pdf格式的文件')
|
||||
_this.clearInputFile()
|
||||
return
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append('file', file, file.name)
|
||||
/*axios.post("/platform/sys/file/uploadDynamicReturnUrl", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})*/
|
||||
axios.post("/platform/sys/file/uploadDynamicReturnUrl", formData, {}).then(response => {
|
||||
console.log(response.data)
|
||||
if (response.data.code === 0) {
|
||||
const html = `<a target="_blank" href="${window.location.origin}${response.data.data}">
|
||||
${file.name}
|
||||
</a>`
|
||||
_this.editor.txt.html(html)
|
||||
} else {
|
||||
alert(response.data.msg + ',上传pdf失败,请联系管理员!')
|
||||
_this.clearInputFile()
|
||||
}
|
||||
}).catch(error => {
|
||||
console.log(error)
|
||||
_this.clearInputFile()
|
||||
alert('上传pdf失败,请联系管理员!')
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
clearInputFile() {
|
||||
const obj = document.getElementById('fileInput');
|
||||
if(obj) {
|
||||
obj.outerHTML = obj.outerHTML
|
||||
}
|
||||
}
|
||||
|
||||
tryChangeActive() {
|
||||
this.active()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//注册上传word按钮
|
||||
class DocMenu extends BtnMenu {
|
||||
constructor(editor) {
|
||||
const $elem = wangEditor.$(
|
||||
`<div class="w-e-menu" data-title="上传文档">
|
||||
<i class="fa fa-file-word-o" aria-hidden="true" /></i>
|
||||
</div>`
|
||||
)
|
||||
super($elem, editor)
|
||||
}
|
||||
|
||||
clickHandler() {
|
||||
const fileInputId = this.editor.textElemId + "input-file"
|
||||
const existingInput = document.getElementById(fileInputId)
|
||||
if (existingInput) {
|
||||
existingInput.remove()
|
||||
}
|
||||
|
||||
const input = document.createElement("input")
|
||||
input.type = "file"
|
||||
input.accept = "application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
input.id = fileInputId
|
||||
input.style.display = "none"
|
||||
input.addEventListener("change", (event) => {
|
||||
const files = event.target.files
|
||||
if (!files || files.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let loading = null
|
||||
if (window.ELEMENT) {
|
||||
loading = ELEMENT.Loading.service({
|
||||
lock: true,
|
||||
text: "文件读取中",
|
||||
spinner: "el-icon-loading",
|
||||
background: "rgba(0, 0, 0, 0.7)"
|
||||
})
|
||||
} else if (window.vant) {
|
||||
loading = vant.Toast.loading({
|
||||
message: "文件读取中",
|
||||
forbidClick: true,
|
||||
overlay: true,
|
||||
duration: 0
|
||||
})
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
formData.append("file", files[0])
|
||||
axios
|
||||
.post("/platform/sys/file/convertHtml", formData, {
|
||||
headers: { "Content-Type": "multipart/form-data", "x-requested-with": "XMLHttpRequest" }
|
||||
})
|
||||
.then((response) => {
|
||||
loading.close()
|
||||
if (response.data.code === 0) {
|
||||
this.editor.txt.html(response.data.data)
|
||||
} else {
|
||||
if (window.ELEMENT) {
|
||||
this.$message.error(response.data.msg)
|
||||
} else if (window.vant) {
|
||||
vant.Toast(response.data.msg)
|
||||
} else {
|
||||
alert(response.data.msg)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.log(error)
|
||||
alert("上传word失败,请联系管理员!")
|
||||
})
|
||||
.finally(() => {
|
||||
input.remove()
|
||||
})
|
||||
})
|
||||
input.click()
|
||||
document.body.appendChild(input)
|
||||
}
|
||||
|
||||
tryChangeActive() {}
|
||||
}
|
||||
|
||||
wangEditor.registerMenu("docMenu", DocMenu)
|
||||
wangEditor.registerMenu('pdfMenuKey', PDFMenu)
|
||||
module.exports = {
|
||||
name: "textEditor",
|
||||
props: {
|
||||
content: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
// 定义值属性,用于接收父组件传递的值
|
||||
value: {
|
||||
type: String,
|
||||
default: ""
|
||||
},
|
||||
height: {
|
||||
type: Number,
|
||||
default: 300
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [
|
||||
"head", //标题
|
||||
"bold", //加粗
|
||||
"fontSize", //字号
|
||||
"fontName", //字体
|
||||
"italic", //斜体
|
||||
"underline", //下划线
|
||||
"strikeThrough", //删除线
|
||||
"indent", //缩进
|
||||
"lineHeight", //行高
|
||||
"foreColor", //文字颜色
|
||||
"backColor", //背景颜色
|
||||
//'link',//链接
|
||||
"list", //序列
|
||||
"todo", //待办
|
||||
"justify", //对齐
|
||||
"quote", //引用
|
||||
// 'emoticon',//表情
|
||||
"image",
|
||||
//'video',
|
||||
"table",
|
||||
//'code',
|
||||
"splitLine", //分割线
|
||||
"undo",
|
||||
"redo"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editor: null,
|
||||
cursorPos: 0
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.initEditor()
|
||||
},
|
||||
|
||||
watch: {
|
||||
value: {
|
||||
handler: function (newValue) {
|
||||
this.$nextTick(() => {
|
||||
const content = newValue || ""
|
||||
if (this.editor.txt.html() !== content) {
|
||||
const selection = this.editor.selection
|
||||
const range = selection.getRange() // 保存当前选区
|
||||
this.editor.txt.html(content)
|
||||
// 如果之前有选区,则恢复光标位置
|
||||
if (range) {
|
||||
selection.restoreSelection()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
initEditor() {
|
||||
let _this = this
|
||||
this.editor = new wangEditor(this.$refs.editorRef)
|
||||
this.editor.config.focus = false
|
||||
//内容change回调
|
||||
this.editor.config.onchange = (newHtml) => {
|
||||
this.cursorPos = this.editor.selection.getCursorPos()
|
||||
const range = this.editor.selection.getRange()
|
||||
range.setStart(range.startContainer, Math.max(this.cursorPos, range.endOffset))
|
||||
this.$emit("input", newHtml)
|
||||
}
|
||||
this.editor.config.linkImgCheck = function (imgSrc, alt, href) {
|
||||
// 1. 返回 true ,说明检查通过
|
||||
return true
|
||||
}
|
||||
|
||||
//配置图片上传
|
||||
this.editor.config.customUploadImg = function (resultFiles, insertImgFn) {
|
||||
// resultFiles 是 input 中选中的文件列表
|
||||
// insertImgFn 是获取图片 url 后,插入到编辑器的方法
|
||||
Promise.all(_this.uploadFiles(resultFiles)).then((res) => {
|
||||
res.forEach((v) => {
|
||||
if (v.data && v.data.code === 0) {
|
||||
insertImgFn(v.data.data)
|
||||
} else {
|
||||
this.$message.error("图片上传失败")
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
this.editor.config.pasteFilterStyle = false
|
||||
this.editor.config.height = this.height
|
||||
this.editor.config.menus = this.menus
|
||||
this.editor.create()
|
||||
},
|
||||
uploadFiles(resultFiles) {
|
||||
return resultFiles.map(async (file) => {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
return axios.post("/platform/sys/file/uploadDynamicReturnUrl", formData, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,207 @@
|
||||
<template>
|
||||
<div>
|
||||
<van-uploader
|
||||
v-model="fileList"
|
||||
:before-read="beforeRead"
|
||||
:after-read="afterRead"
|
||||
:before-delete="beforeDelete"
|
||||
multiple
|
||||
progress
|
||||
:accept="accept"
|
||||
:max-count="upload_number"
|
||||
title=""
|
||||
description=""
|
||||
tip=""
|
||||
></van-uploader>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "h5Index",
|
||||
props: {
|
||||
// 上传返回id
|
||||
upload_return_id_api: {
|
||||
type: String,
|
||||
default: "/platform/sys/file/uploadLocalReturnId",
|
||||
required: false
|
||||
},
|
||||
// 上传返回url
|
||||
upload_dynamic_return_url_api: {
|
||||
type: String,
|
||||
default: "/platform/sys/file/uploadDynamicReturnUrl",
|
||||
required: false
|
||||
},
|
||||
// 当上传接口为id的情况下,配置下载接口
|
||||
upload_id_download_url: {
|
||||
type: String,
|
||||
default: "/platform/sys/file/download?id=",
|
||||
required: false
|
||||
},
|
||||
// 上传样式或图片方式 file || drag || image
|
||||
upload_mode: {
|
||||
type: String,
|
||||
default: "file",
|
||||
required: false
|
||||
},
|
||||
// 上传数量
|
||||
upload_number: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
required: false
|
||||
},
|
||||
// 上传返回id或url
|
||||
upload_result_type: {
|
||||
type: String,
|
||||
default: "url",
|
||||
required: false
|
||||
},
|
||||
// 上传返回分类 数组或字符串逗号隔离 interval | array
|
||||
upload_result_category: {
|
||||
type: String,
|
||||
default: "interval",
|
||||
required: false
|
||||
},
|
||||
// 跟antdv官方一样,是否显示文件列表
|
||||
show_upload_list: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
// 跟antdv官方一样,接受上传的文件类型
|
||||
accept: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false
|
||||
},
|
||||
// 是否是完整的结果(就是文件上传返回什么,该组件返回什么,uploadResultCategory必须为array)
|
||||
complete_result: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
// 父组件传来的参数
|
||||
value: {
|
||||
type: [String, Array],
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileList: [
|
||||
// { url: "http://localhost:8080/platform/sys/file/download?id=rn1v1efh2ag0aps59ult3ebudf", isImage: true }
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler: function (val) {
|
||||
if (val) {
|
||||
if (Array.isArray(val)) {
|
||||
this.fileList = val.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
status: null,
|
||||
isImage: ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(v.name.split(".").pop().toLowerCase())
|
||||
}
|
||||
})
|
||||
} else if(typeof val === "string") {
|
||||
this.fileList = [
|
||||
{
|
||||
url: val,
|
||||
status: null,
|
||||
isImage: true
|
||||
}
|
||||
]
|
||||
} else {
|
||||
val = JSON.parse(val)
|
||||
this.fileList = val.map((v) => {
|
||||
return {
|
||||
...v,
|
||||
url: v.url ? v.url : v.response?.data,
|
||||
isImage: ["jpg", "jpeg", "png", "gif", "bmp", "webp"].includes(v.name.split(".").pop().toLowerCase()),
|
||||
status: null
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
this.fileList = []
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
action() {
|
||||
return this.upload_result_type === "id" ? this.upload_return_id_api : this.upload_dynamic_return_url_api
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
beforeDelete(file) {
|
||||
this.fileList = this.fileList.filter((f) => f.url !== file.url)
|
||||
this.$emit("update:value", this.fileList)
|
||||
},
|
||||
beforeRead(file) {
|
||||
return true
|
||||
},
|
||||
afterRead(files) {
|
||||
this.fileList.map((f) => {
|
||||
if (f.file && f.file.name === files.file.name) {
|
||||
f.status = "loading"
|
||||
}
|
||||
})
|
||||
const uploadPromises = this.fileList
|
||||
.filter((f) => f.status === "loading")
|
||||
.map((f) => {
|
||||
const formData = new FormData()
|
||||
formData.append("file", f.file)
|
||||
return this.$axios.post(this.action, formData).then((resp) => {
|
||||
if (resp.code === 0) {
|
||||
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
|
||||
} else {
|
||||
f.status = "fail"
|
||||
}
|
||||
})
|
||||
})
|
||||
Promise.all(uploadPromises)
|
||||
.then(() => {
|
||||
if (this.upload_result_category === "interval") {
|
||||
} else if (this.upload_result_category === "array") {
|
||||
if (this.complete_result) {
|
||||
this.$emit("update:value", this.fileList)
|
||||
} else {
|
||||
const resultArrayValue = []
|
||||
this.fileList.forEach((data) => {
|
||||
resultArrayValue.push(data.response.data)
|
||||
})
|
||||
this.$emit("update:value", resultArrayValue)
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("所有上传请求失败:", error)
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.van-uploader__title {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.van-uploader__wrapper {
|
||||
margin-left: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,646 @@
|
||||
<template>
|
||||
<div class="upload_file">
|
||||
<el-upload
|
||||
v-if="upload_mode === 'file'"
|
||||
:file-list="fileList"
|
||||
:action="action"
|
||||
:on-change="handleChange"
|
||||
:on-success="handleSuccess"
|
||||
:on-preview="handlePreview"
|
||||
:on-exceed="handleExceed"
|
||||
:before-upload="beforeUpload"
|
||||
:limit="upload_number"
|
||||
:accept="fileAccept"
|
||||
:multiple="true"
|
||||
>
|
||||
<el-button :size="upload_button_size" type="primary">
|
||||
{{ upload_text }}
|
||||
</el-button>
|
||||
<div class="el-upload__tip" v-html="upload_tips"></div>
|
||||
</el-upload>
|
||||
|
||||
<!-- 图片上传模式 -->
|
||||
<el-upload
|
||||
v-if="upload_mode === 'image'"
|
||||
:file-list="fileList"
|
||||
:action="action"
|
||||
:on-change="handleChange"
|
||||
:on-success="handleSuccess"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:before-upload="beforeUpload"
|
||||
:on-exceed="handleExceed"
|
||||
:limit="upload_number"
|
||||
:accept="fileAccept"
|
||||
:multiple="true"
|
||||
list-type="picture-card"
|
||||
>
|
||||
<i class="el-icon-plus"></i>
|
||||
<!-- <div class="el-upload__tip" v-if="fileList.length < upload_number">-->
|
||||
<!-- <div style="margin-top: 8px">{{ upload_text }}</div>-->
|
||||
<!-- </div>-->
|
||||
</el-upload>
|
||||
|
||||
<!-- 拖拽上传模式 -->
|
||||
|
||||
<template v-if="upload_mode === 'drag'">
|
||||
<el-tabs class="mb10" v-model="uploadMode">
|
||||
<el-tab-pane label="电脑选择文件" name="pc">
|
||||
<span slot="label">
|
||||
<i class="fa fa-desktop"></i>
|
||||
电脑选择文件
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="手机扫码上传" name="mobile">
|
||||
<span slot="label">
|
||||
<i class="fa fa-qrcode"></i>
|
||||
手机扫码上传
|
||||
</span>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-upload
|
||||
ref="dragUploadRef"
|
||||
drag
|
||||
:file-list="fileList"
|
||||
:multiple="true"
|
||||
:action="action"
|
||||
:on-change="handleChange"
|
||||
:on-success="handleSuccess"
|
||||
:on-preview="handlePreview"
|
||||
:on-remove="handleRemove"
|
||||
:on-exceed="handleExceed"
|
||||
:before-upload="beforeUpload"
|
||||
:limit="upload_number"
|
||||
:accept="fileAccept"
|
||||
class="drag-upload"
|
||||
:class="{ 'drag-upload-disabled': uploadMode !== 'pc' }"
|
||||
>
|
||||
<div slot="trigger" style="height: 134px" v-if="uploadMode === 'pc'">
|
||||
<i class="el-icon-upload"></i>
|
||||
<div class="el-upload__text">{{ upload_text }}</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="h5-qrcode" v-if="uploadMode === 'mobile'" v-loading="codeLoading">
|
||||
<QrCode :options="{ width: 126 }" :value="qrCodeAddress" class="signature-qrcode"></QrCode>
|
||||
</div>
|
||||
</div>
|
||||
<div class="el-upload__tip" v-html="upload_tips"></div>
|
||||
</el-upload>
|
||||
</template>
|
||||
|
||||
<!-- 插槽,用于显示额外的说明信息 -->
|
||||
<slot name="explain"></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script nonce="${cspNonce!}">
|
||||
module.exports = {
|
||||
props: {
|
||||
// 上传返回id
|
||||
upload_return_id_api: {
|
||||
type: String,
|
||||
default: "/platform/sys/file/uploadLocalReturnId",
|
||||
required: false
|
||||
},
|
||||
// 上传返回url
|
||||
upload_dynamic_return_url_api: {
|
||||
type: String,
|
||||
default: "/platform/sys/file/uploadDynamicReturnUrl",
|
||||
required: false
|
||||
},
|
||||
// 当上传接口为id的情况下,配置下载接口
|
||||
upload_id_download_url: {
|
||||
type: String,
|
||||
default: "/platform/sys/file/download?id=",
|
||||
required: false
|
||||
},
|
||||
// 上传样式或图片方式 file || drag || image
|
||||
upload_mode: {
|
||||
type: String,
|
||||
default: "file",
|
||||
required: false
|
||||
},
|
||||
// 上传数量
|
||||
upload_number: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
required: false
|
||||
},
|
||||
// 上传文件大小限制
|
||||
upload_size: {
|
||||
type: Number,
|
||||
default: 1024 * 1024 * 10,
|
||||
required: false
|
||||
},
|
||||
// 上传按钮文字
|
||||
upload_text: {
|
||||
type: String,
|
||||
default: "点击上传文件",
|
||||
required: false
|
||||
},
|
||||
// 上传按钮大小
|
||||
upload_button_size: {
|
||||
type: String,
|
||||
default: "small",
|
||||
required: false
|
||||
},
|
||||
// 上传返回id或url
|
||||
upload_result_type: {
|
||||
type: String,
|
||||
default: "url",
|
||||
required: false
|
||||
},
|
||||
// 上传返回分类 数组或字符串逗号隔离 interval | array
|
||||
upload_result_category: {
|
||||
type: String,
|
||||
default: "interval",
|
||||
required: false
|
||||
},
|
||||
// 是否显示文件列表
|
||||
show_upload_list: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
required: false
|
||||
},
|
||||
// 接受上传的文件类型
|
||||
accept: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: false
|
||||
},
|
||||
// 是否是完整的结果(就是文件上传返回什么,该组件返回什么,uploadResultCategory必须为array)
|
||||
complete_result: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
required: false
|
||||
},
|
||||
// 父组件传来的参数
|
||||
value: {
|
||||
type: [String, Array],
|
||||
default: undefined,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
fileList: [],
|
||||
previewVisible: false,
|
||||
previewImage: "",
|
||||
fileAccept: null,
|
||||
dragDisabled: true,
|
||||
qrCodeAddress: origin + "/platform/sys/h5ScanCodeUploadFile",
|
||||
uploadMode: "pc",
|
||||
codeLoading: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
action() {
|
||||
return this.upload_result_type === "id" ? this.upload_return_id_api : this.upload_dynamic_return_url_api
|
||||
},
|
||||
upload_tips() {
|
||||
const uploadNumber = this.upload_number
|
||||
const fileAccept = this.fileAccept
|
||||
const fileSize = this.upload_size
|
||||
|
||||
let tips = null
|
||||
if (uploadNumber) {
|
||||
tips = `只能上传<span style="color: red">${uploadNumber}</span>个文件;`
|
||||
}
|
||||
if (fileAccept) {
|
||||
tips = tips + `只能上传<span style="color: red">${fileAccept}</span>文件;`
|
||||
} else {
|
||||
tips = tips + '不限格式;'
|
||||
}
|
||||
if (fileSize) {
|
||||
tips = tips + `单个文件大小不能超过<span style="color: red">${fileSize / 1024 / 1024}</span>M;`
|
||||
}
|
||||
return tips
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
buildFileObject(url, id) {
|
||||
return {
|
||||
data: url ? url : this.upload_id_download_url + id,
|
||||
name: url ? url : id,
|
||||
url: url ? url : this.upload_id_download_url + id,
|
||||
status: "success",
|
||||
response: {
|
||||
data: url ? url : id,
|
||||
code: 200
|
||||
}
|
||||
}
|
||||
},
|
||||
echo(newVal) {
|
||||
// 字符串隔离情况
|
||||
if (this.upload_result_category === "interval") {
|
||||
// id隔离
|
||||
if (this.upload_result_type === "id") {
|
||||
newVal.split(",").forEach((id) => {
|
||||
const file = this.buildFileObject(undefined, id)
|
||||
this.fileList.push(file)
|
||||
this.fileList.reverse()
|
||||
})
|
||||
}
|
||||
// url隔离
|
||||
if (this.upload_result_type === "url") {
|
||||
newVal.split(",").forEach((url) => {
|
||||
const file = this.buildFileObject(url)
|
||||
this.fileList.push(file)
|
||||
this.fileList.reverse()
|
||||
})
|
||||
}
|
||||
}
|
||||
// 如果是数组的情况下
|
||||
if (this.upload_result_category === "array") {
|
||||
if (this.complete_result) {
|
||||
// 得去掉数组里面的thumbUrl,一个base64太大,无用
|
||||
// let newResult = cloneDeep(newVal)
|
||||
let newResult = JSON.parse(JSON.stringify(newVal))
|
||||
newResult.map((e) => {
|
||||
if (e.thumbUrl) {
|
||||
delete e.thumbUrl
|
||||
}
|
||||
if (this.upload_result_type === "id") {
|
||||
e.url = this.upload_id_download_url + e.response.data
|
||||
}
|
||||
if (this.upload_result_type === "url") {
|
||||
e.url = e.response.data
|
||||
}
|
||||
})
|
||||
this.fileList = newResult
|
||||
} else {
|
||||
// id数组
|
||||
if (this.upload_result_type === "id") {
|
||||
if (Array.isArray(newVal)) {
|
||||
newVal.forEach((id) => {
|
||||
this.fileList.push(this.buildFileObject(undefined, id))
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
JSON.parse(newVal).forEach((id) => {
|
||||
this.fileList.push(this.buildFileObject(undefined, id))
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
// url数组
|
||||
if (this.upload_result_type === "url") {
|
||||
if (Array.isArray(newVal)) {
|
||||
newVal.forEach((url) => {
|
||||
this.fileList.push(this.buildFileObject(url))
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
JSON.parse(newVal).forEach((url) => {
|
||||
this.fileList.push(this.buildFileObject(url))
|
||||
})
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// 这是兜底逻辑,保准只让这个image类型上传图片
|
||||
beforeUpload(file) {
|
||||
|
||||
if (file.size > this.upload_size) {
|
||||
this.$message.error("文件过大,请上传小于" + (this.upload_size / 1024 / 1024).toFixed(0) + "M的文件!")
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
return true
|
||||
|
||||
// if (!this.fileAccept || typeof this.fileAccept !== "string") {
|
||||
// return true
|
||||
// }
|
||||
//
|
||||
// // 去除前后空格并转换为数组
|
||||
// const acceptTypes = this.fileAccept
|
||||
// .trim()
|
||||
// .split(",")
|
||||
// .map((type) => type.trim().toUpperCase())
|
||||
//
|
||||
// let fileType
|
||||
// const dotIndex = file.name.lastIndexOf(".")
|
||||
// if (dotIndex !== -1) {
|
||||
// fileType = file.name.substring(dotIndex).toUpperCase()
|
||||
// } else {
|
||||
// this.$message.error("文件必须包含有效的扩展名!")
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// const valid = acceptTypes.includes(fileType)
|
||||
// if (valid) {
|
||||
// return true
|
||||
// }
|
||||
// this.$message.error(`文件只能是 ${this.fileAccept} 格式!`)
|
||||
// return false
|
||||
},
|
||||
|
||||
// 预览图片
|
||||
handlePreview(file) {
|
||||
const suffix = file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase()
|
||||
const fileId = file.response.data.substring(file.response.data.lastIndexOf("=") + 1)
|
||||
const buildFile = {
|
||||
id: fileId,
|
||||
suffix,
|
||||
name: file.name,
|
||||
downloadPath: file.response.data
|
||||
}
|
||||
this.$commonUtil.previewFile(buildFile)
|
||||
},
|
||||
|
||||
handleRemove(file, fileList) {
|
||||
if (this.upload_result_category === "interval") {
|
||||
if (this.upload_result_type === "id") {
|
||||
}
|
||||
if (this.upload_result_type === "url") {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.upload_result_category === "array") {
|
||||
if (this.complete_result) {
|
||||
this.$emit("update:value", fileList)
|
||||
} else {
|
||||
if (this.upload_result_type === "id") {
|
||||
this.$emit(
|
||||
"update:value",
|
||||
fileList.map((e) => e.response.data)
|
||||
)
|
||||
} else {
|
||||
this.$emit(
|
||||
"update:value",
|
||||
fileList.map((e) => e.response.data)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.$emit("update:value", fileList)
|
||||
}
|
||||
},
|
||||
|
||||
handleExceed(files, fileList) {
|
||||
this.$message.warning("最多只能上传" + this.upload_number + "个文件")
|
||||
},
|
||||
|
||||
// 上传事件
|
||||
handleChange(file, fileList) {
|
||||
//
|
||||
// let result = []
|
||||
// // const file = uploads.file
|
||||
// debugger
|
||||
// if (file && (file.status === 'done' || file.status === 'removed') && file.response && file.response.code === 200) {
|
||||
// uploads.fileList.forEach((f) => {
|
||||
// result.push(f)
|
||||
// })
|
||||
// }
|
||||
// if (result.length > 0) {
|
||||
// if (this.upload_result_category === 'interval') {
|
||||
// let resultIntervalValue = ''
|
||||
// result.forEach((data) => {
|
||||
// resultIntervalValue =
|
||||
// data.response.data + (resultIntervalValue ? ',' + resultIntervalValue : '')
|
||||
// })
|
||||
// this.$emit('update:value', resultIntervalValue)
|
||||
// this.$emit('onChange', resultIntervalValue)
|
||||
// } else if (this.upload_result_category === 'array') {
|
||||
// if (this.complete_result) {
|
||||
// // 得去掉数组里面的thumbUrl,一个base64太大,无用
|
||||
// let newResult = JSON.parse(JSON.stringify(newResult))
|
||||
// newResult.map((e) => {
|
||||
// if (e.thumbUrl) {
|
||||
// delete e.thumbUrl
|
||||
// }
|
||||
// })
|
||||
// this.emit('update:value', newResult)
|
||||
// this.emit('onChange', newResult)
|
||||
// } else {
|
||||
// const resultArrayValue = []
|
||||
// result.forEach((data) => {
|
||||
// resultArrayValue.push(data.response.data)
|
||||
// })
|
||||
// this.emit('update:value', resultArrayValue)
|
||||
// this.emit('onChange', resultArrayValue)
|
||||
// }
|
||||
// }
|
||||
// return
|
||||
// }
|
||||
// this.$emit('update:value', undefined)
|
||||
// this.$emit('onChange', undefined)
|
||||
},
|
||||
|
||||
handleSuccess(response, file, fileList) {
|
||||
let result = []
|
||||
if (response && response.code === 0 && file.status === "success") {
|
||||
fileList.forEach((f) => {
|
||||
if(f.response.data?.includes('webvpn')) {
|
||||
const index = f.response.data.indexOf('/platform')
|
||||
f.response.data = f.response.data.substring(index)
|
||||
}
|
||||
result.push(f)
|
||||
})
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
fileList = fileList.splice(file, 1)
|
||||
}
|
||||
if (result.length > 0) {
|
||||
if (this.upload_result_category === "interval") {
|
||||
const resultArrayValue = []
|
||||
result.forEach((data) => {
|
||||
resultArrayValue.push(data.response.data)
|
||||
})
|
||||
this.$emit("update:value", resultArrayValue.join(","))
|
||||
} else if (this.upload_result_category === "array") {
|
||||
if (this.complete_result) {
|
||||
// 得去掉数组里面的thumbUrl,一个base64太大,无用
|
||||
let newResult = JSON.parse(JSON.stringify(result))
|
||||
newResult.map((e) => {
|
||||
if (!e.url) {
|
||||
e.url = e.response.data
|
||||
}
|
||||
if (e.thumbUrl) {
|
||||
delete e.thumbUrl
|
||||
}
|
||||
})
|
||||
this.$emit("update:value", newResult)
|
||||
} else {
|
||||
const resultArrayValue = []
|
||||
result.forEach((data) => {
|
||||
resultArrayValue.push(data.response.data)
|
||||
})
|
||||
this.$emit("update:value", resultArrayValue)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
this.$emit("update:value", fileList)
|
||||
},
|
||||
|
||||
// 通过DOM获取上传的文件
|
||||
uploadFileList() {
|
||||
if (this.fileList) {
|
||||
const result = []
|
||||
// 只返回这些就够用了,其他基本用不到
|
||||
this.fileList.value.forEach((item) => {
|
||||
const obj = {
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
size: item.size,
|
||||
url: item.response.data
|
||||
}
|
||||
result.push(obj)
|
||||
})
|
||||
return result
|
||||
} else {
|
||||
return []
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler: function (newVal) {
|
||||
if (this.value && newVal) {
|
||||
this.fileList = []
|
||||
this.echo(newVal)
|
||||
} else {
|
||||
this.fileList = []
|
||||
this.$emit("update:value", undefined)
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
upload_mode: {
|
||||
handler: function (newVal) {
|
||||
if (newVal && newVal === "image") {
|
||||
if (this.accept) {
|
||||
this.fileAccept = this.accept
|
||||
} else {
|
||||
this.fileAccept = "image/*"
|
||||
}
|
||||
} else {
|
||||
this.fileAccept = this.accept
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
accept: {
|
||||
handler: function (newVal) {
|
||||
if (newVal) {
|
||||
this.fileAccept = newVal
|
||||
} else {
|
||||
this.fileAccept = this.accept
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
const timestamp = new Date().getTime()
|
||||
this.qrCodeAddress = origin + "/platform/sys/h5ScanCodeUploadFile?timestamp=" + timestamp
|
||||
console.info("qrCodeAddress:", this.qrCodeAddress)
|
||||
// 可选:轮询或 watch ready 状态
|
||||
const checkReady = () => {
|
||||
if (webSocketPubSub.ws && webSocketPubSub.ws.readyState === WebSocket.OPEN) {
|
||||
console.info("ws.readyState is open");
|
||||
this.codeLoading = false;
|
||||
} else {
|
||||
setTimeout(checkReady, 100);
|
||||
}
|
||||
};
|
||||
checkReady();
|
||||
webSocketPubSub.subscribe("h5-scan-code-upload-file-" + timestamp, (data) => {
|
||||
console.log("ws:收到手机上传文件消息:", data)
|
||||
const originFileList = this.fileList || []
|
||||
console.log(data.files)
|
||||
|
||||
if (this.upload_number) {
|
||||
if (originFileList.length + data.files.length > this.upload_number) {
|
||||
this.$message.error("上传文件数量超出限制")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (this.fileAccept) {
|
||||
// 去除前后空格并转换为数组
|
||||
const acceptTypes = this.fileAccept
|
||||
.trim()
|
||||
.split(",")
|
||||
.map((type) => type.trim().toUpperCase())
|
||||
|
||||
const validFiles = data.files.filter((file) => {
|
||||
let fileType
|
||||
const dotIndex = file.name.lastIndexOf(".")
|
||||
if (dotIndex !== -1) {
|
||||
fileType = file.name.substring(dotIndex).toUpperCase()
|
||||
}
|
||||
return acceptTypes.includes(fileType)
|
||||
})
|
||||
|
||||
if (validFiles.length < data.files.length) {
|
||||
data.files = validFiles
|
||||
this.$message.error(`文件只能是 ${this.fileAccept} 格式,已自动过滤掉不符合要求文件!`)
|
||||
}
|
||||
}
|
||||
|
||||
this.$message.success("获取手机文件成功")
|
||||
|
||||
const newFileList = originFileList.concat(data.files)
|
||||
this.$emit("update:value", newFileList)
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.upload_file {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.upload_file .el-upload {
|
||||
/*width: 100%;*/
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.upload_file .el-upload .el-upload-dragger {
|
||||
width: 100%;
|
||||
line-height: 26px;
|
||||
height: auto;
|
||||
border: 1px solid var(--border-color-base);
|
||||
}
|
||||
|
||||
.el-upload-dragger .el-icon-upload {
|
||||
margin: 10px 0 16px;
|
||||
}
|
||||
|
||||
.drag-upload .el-upload {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.upload_file .drag-upload-disabled > .el-upload {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.el-upload--picture-card {
|
||||
width: var(--upload-width, 148px) !important;
|
||||
height: var(--upload-height, 148px) !important;
|
||||
/*line-height: calc(var(--upload-height, 148px) - 2px) !important;*/
|
||||
line-height: unset !important;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.el-upload-list--picture-card .el-upload-list__item {
|
||||
width: var(--upload-width, 148px);
|
||||
height: var(--upload-height, 148px);
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,146 @@
|
||||
<template>
|
||||
<div class="user-audit-opinion-textarea">
|
||||
<el-input type="textarea" :rows="3" v-model="content" v-bind="$attrs" @change="onChange" @blur="onBlur"></el-input>
|
||||
<div class="opinion-wrap">
|
||||
<div class="title">常用审核意见:</div>
|
||||
<el-select v-model="selectOpinion" placeholder="可选择常用审核意见" size="small" @change="selectOpinionChange">
|
||||
<el-option v-for="item in opinionOptions" :key="item.id" :label="item.text" :value="item.text"></el-option>
|
||||
</el-select>
|
||||
<div class="settings">
|
||||
<el-link type="primary" @click="openSetting">自定义</el-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog title="设置常用意见" :visible.sync="dialogVisible" width="50%" append-to-body>
|
||||
<el-table :data="tableOpinionData" key="id">
|
||||
<el-table-column label="序号" type="index" width="50"></el-table-column>
|
||||
<el-table-column label="意见内容">
|
||||
<template slot-scope="scope">
|
||||
<el-input v-model="scope.row.text" placeholder="请输入意见内容" max="50"></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
<template #header>
|
||||
<el-button type="primary" size="mini" @click="pushNewOpinion">添加</el-button>
|
||||
</template>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="danger" size="mini" @click="tableOpinionData.splice(scope.$index, 1)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="updateOpinion">提交</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "index",
|
||||
props: {
|
||||
value: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
content: null,
|
||||
tableOpinionData: [],
|
||||
opinionOptions: [],
|
||||
selectOpinion: null,
|
||||
dialogVisible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler: function (val) {
|
||||
this.content = val
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onChange(val) {
|
||||
this.$emit("input", val)
|
||||
},
|
||||
onBlur(val) {
|
||||
this.$emit("blur", this.content)
|
||||
},
|
||||
selectOpinionChange(val) {
|
||||
this.$emit("input", val)
|
||||
},
|
||||
openSetting() {
|
||||
this.dialogVisible = true
|
||||
this.listOpinion()
|
||||
},
|
||||
pushNewOpinion() {
|
||||
if (this.tableOpinionData.length === 10) {
|
||||
this.$message.error("最多可添加10条审批意见")
|
||||
return
|
||||
}
|
||||
|
||||
const id = Math.max(...this.tableOpinionData.map((v) => v.id), 0)
|
||||
this.tableOpinionData.push({ id: id + 1, text: "" })
|
||||
},
|
||||
updateOpinion() {
|
||||
this.$axios
|
||||
.post("/platform/sys/userApproval/opinion/update", {
|
||||
opinions: JSON.stringify(this.tableOpinionData)
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.$message.success("设置成功")
|
||||
this.dialogVisible = false
|
||||
}
|
||||
})
|
||||
},
|
||||
listOpinion() {
|
||||
this.$axios.get("/platform/sys/userApproval/opinion/list").then((res) => {
|
||||
if (res.code === 0) {
|
||||
this.tableOpinionData = res.data || []
|
||||
this.opinionOptions = res.data || []
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.listOpinion()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.user-audit-opinion-textarea {
|
||||
border: 1px solid var(--border-color-base);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.user-audit-opinion-textarea:focus {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
.user-audit-opinion-textarea textarea {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.user-audit-opinion-textarea:focus {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.user-audit-opinion-textarea .opinion-wrap .title {
|
||||
font-weight: 500;
|
||||
color: #000000;
|
||||
}
|
||||
|
||||
.opinion-wrap {
|
||||
padding: 5px 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
}
|
||||
|
||||
.opinion-wrap .settings {
|
||||
text-align: right;
|
||||
margin-left: 5px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<el-select v-model="selectValue" :clearable="clearable" filterable remote :remote-method="selectUser"
|
||||
placeholder="请输入姓名或工号查询" @change="onChange" v-bind="$attrs" style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
v-if="!item.disabled"
|
||||
:key="item[option_value]"
|
||||
:label="option_label_func ? option_label_func(item) : item[option_label]"
|
||||
:value="item[option_value]"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
model: {
|
||||
prop: "value",
|
||||
event: "change"
|
||||
},
|
||||
props: {
|
||||
value: {type: [String, Array]},
|
||||
|
||||
api: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "/open/common/userOptions"
|
||||
},
|
||||
|
||||
api_params: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {
|
||||
return {}
|
||||
}
|
||||
},
|
||||
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
api_input_key_name: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: "keyWord"
|
||||
},
|
||||
|
||||
option_list: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
|
||||
option_value: {
|
||||
type: String,
|
||||
default: "id"
|
||||
},
|
||||
|
||||
option_label: {
|
||||
type: String,
|
||||
default: "username"
|
||||
},
|
||||
|
||||
option_label_func: {
|
||||
type: Function,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
options: [],
|
||||
selectValue: null
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
value: {
|
||||
handler: function (val) {
|
||||
if (this.$attrs.multiple) {
|
||||
this.selectValue = [...val]
|
||||
} else {
|
||||
this.selectValue = val ? val.toString() : ""
|
||||
}
|
||||
},
|
||||
immediate: true,
|
||||
deep: true
|
||||
},
|
||||
option_list: {
|
||||
handler: function (val) {
|
||||
if (val) {
|
||||
this.options = val
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
selectUser(query) {
|
||||
if (query !== "") {
|
||||
$.get(this.api, {[this.api_input_key_name]: query, ...this.api_params}).then((res) => {
|
||||
this.options = res.data
|
||||
})
|
||||
}
|
||||
},
|
||||
onChange(val) {
|
||||
this.$emit("change", val)
|
||||
},
|
||||
|
||||
clearOptions() {
|
||||
this.options = []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.clearOptions()
|
||||
console.log(this.placeholder)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@@ -0,0 +1,176 @@
|
||||
<template>
|
||||
<van-popup :round="true" :safe-area-inset-bottom="false" :close-on-click-overlay="ableClose" v-model="show">
|
||||
<div class="van-text-dialog__wrapper">
|
||||
<!-- 关闭图标 -->
|
||||
<van-icon v-if="ableClose" role="button" tabindex="0" name="cross" class="van-text-dialog__close-icon" @click="close"></van-icon>
|
||||
|
||||
<!-- 标题 -->
|
||||
<div class="van-text-dialog__title">
|
||||
<slot name="title">{{ title }}</slot>
|
||||
</div>
|
||||
|
||||
<!-- 内容主体 -->
|
||||
<div class="van-text-dialog__body">
|
||||
<template v-if="$slots.body">
|
||||
<slot name="body"></slot>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-if="typeof body === 'string'" class="van-text-dialog__content">
|
||||
{{ body }}
|
||||
</div>
|
||||
<template v-else-if="Array.isArray(body)">
|
||||
<div v-for="(item, index) in body" :key="index" class="van-text-dialog__part">
|
||||
<div v-if="item.title" class="van-text-dialog__subtitle">
|
||||
{{ item.title }}
|
||||
</div>
|
||||
<div class="van-text-dialog__content">
|
||||
{{ item.content }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 底部线 & 遮罩 -->
|
||||
<div v-if="mask" class="van-text-dialog__line van-hairline--bottom">
|
||||
<div class="van-text-dialog__mask"></div>
|
||||
</div>
|
||||
|
||||
<!-- 复选框 -->
|
||||
<div v-if="checkboxLabel || $slots.checkboxLabel" class="van-text-dialog__checkbox">
|
||||
<van-checkbox plain :icon-size="16" :disabled="!ableCheck" v-model="proxyCheck">
|
||||
<slot name="checkbox-label">{{ checkboxLabel }}</slot>
|
||||
</van-checkbox>
|
||||
</div>
|
||||
|
||||
<!-- 确认按钮 -->
|
||||
<van-button v-if="buttonText" type="primary" :disabled="!ableConfirm" block class="van-text-dialog__button" @click="confirm">
|
||||
{{ buttonText }}
|
||||
</van-button>
|
||||
|
||||
<!-- 默认插槽 -->
|
||||
<slot></slot>
|
||||
</div>
|
||||
</van-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "vantTextDialog",
|
||||
props: {
|
||||
show: Boolean,
|
||||
title: String,
|
||||
body: [Array, String],
|
||||
mask: Boolean,
|
||||
check: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
checkboxLabel: String,
|
||||
buttonText: String,
|
||||
ableClose: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
ableCheck: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
ableConfirm: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
proxyCheck: {
|
||||
get() {
|
||||
return this.check
|
||||
},
|
||||
set(val) {
|
||||
this.$emit("toggle", val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.$emit("close")
|
||||
},
|
||||
confirm() {
|
||||
this.$emit("confirm")
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.van-text-dialog__wrapper {
|
||||
min-width: 300px;
|
||||
background: #fff;
|
||||
padding: 18px 20px 20px;
|
||||
}
|
||||
.van-text-dialog__close-icon {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
.van-text-dialog__title {
|
||||
margin: 0 14px;
|
||||
line-height: 25px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: #111;
|
||||
font-weight: 700;
|
||||
}
|
||||
.van-text-dialog__body {
|
||||
margin-top: 18px;
|
||||
position: relative;
|
||||
overflow-y: scroll;
|
||||
max-height: 350px;
|
||||
}
|
||||
.van-text-dialog__part {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.van-text-dialog__subtitle {
|
||||
margin-bottom: 4px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
color: #111;
|
||||
}
|
||||
.van-text-dialog__content {
|
||||
margin-bottom: 16px;
|
||||
line-height: 24px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
.van-text-dialog__line {
|
||||
margin-top: 1px;
|
||||
position: relative;
|
||||
}
|
||||
.van-text-dialog__line::after {
|
||||
border-color: #e2e2e2;
|
||||
}
|
||||
.van-text-dialog__mask {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 58px;
|
||||
z-index: 999;
|
||||
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0.2) 50%, #fff 100%);
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.2) 50%, #fff 100%);
|
||||
}
|
||||
.van-text-dialog__checkbox {
|
||||
margin-top: 12px;
|
||||
}
|
||||
.van-text-dialog__checkbox-label {
|
||||
margin-left: 8px;
|
||||
font-size: 15px;
|
||||
color: #666;
|
||||
}
|
||||
.van-text-dialog__button {
|
||||
margin-top: 24px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,46 @@
|
||||
<template>
|
||||
<van-dropdown-item :options="yearOptions" @change="yearChange" v-model="year"></van-dropdown-item>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
module.exports = {
|
||||
name: "year-van-dropdown-item",
|
||||
props: {
|
||||
num: {
|
||||
type: Number,
|
||||
default: 10
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
default: new Date().getFullYear()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler: function (val) {
|
||||
this.year = val
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
yearOptions: [],
|
||||
year: new Date().getFullYear()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
yearChange(val) {
|
||||
this.$emit("input", val)
|
||||
this.$emit("change", val)
|
||||
}
|
||||
},
|
||||
created() {
|
||||
for (let i = new Date().getFullYear() - this.num; i <= new Date().getFullYear(); i++) {
|
||||
this.yearOptions.unshift({ value: i, text: i + "年" })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
Reference in New Issue
Block a user