Initial commit

This commit is contained in:
danshihao
2024-06-11 15:39:01 +08:00
commit 00acf31aa3
1589 changed files with 520803 additions and 0 deletions
+151
View File
@@ -0,0 +1,151 @@
<template>
<div class="home">
<div class="banner">
<img alt="Vue logo" style="width: 64px; height: 64px" src="../assets/logo.png">
<h3 style="margin-top: 1rem">Welcome to Your Vue.js App</h3>
</div>
<br/>
<h2># Trend 组件 </h2>
<a-divider> 正常 </a-divider>
<a-card>
<trend flag="up" style="margin-right: 16px;">
<span slot="term">工资</span>
5%
</trend>
<trend flag="up" style="margin-right: 16px;">
<span slot="term">工作量</span>
50%
</trend>
<trend flag="down">
<span slot="term">身体状态</span>
50%
</trend>
</a-card>
<a-divider> 颜色反转 </a-divider>
<a-card style="margin-bottom: 3rem">
<trend flag="up" :reverse-color="true" style="margin-right: 16px;">
<span slot="term">工资</span>
5%
</trend>
<trend flag="down" :reverse-color="true" style="margin-right: 16px;">
<span slot="term">工作量</span>
50%
</trend>
</a-card>
<h2># AvatarList 组件 </h2>
<a-divider> AvatarList </a-divider>
<a-card style="margin-bottom: 3rem">
<avatar-list :max-length="3">
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
<a-divider type="vertical" style="margin: 0 16px" />
<avatar-list size="mini">
<avatar-list-item tips="Jake" src="https://gw.alipayobjects.com/zos/rmsportal/zOsKZmFRdUtvpqCImOVY.png" />
<avatar-list-item tips="Andy" src="https://gw.alipayobjects.com/zos/rmsportal/sfjbOqnsXXJgNCjCzDBL.png" />
<avatar-list-item tips="Niko" src="https://gw.alipayobjects.com/zos/rmsportal/kZzEzemZyKLKFsojXItE.png" />
</avatar-list>
</a-card>
<h2># CountDown 组件 </h2>
<a-divider> CountDown </a-divider>
<a-card style="margin-bottom: 3rem">
<count-down
style="font-size: 2rem"
:target="new Date().getTime() + 3000000"
:on-end="onEndHandle">
</count-down>
<a-divider type="vertical" style="margin: 0 16px" />
<count-down
style="font-size: 2rem"
:target="new Date().getTime() + 10000"
:on-end="onEndHandle2">
</count-down>
</a-card>
<h2># Ellipsis 组件 </h2>
<a-divider> Ellipsis </a-divider>
<a-card style="margin-bottom: 3rem">
<ellipsis :length="100" tooltip>
There were injuries alleged in three cases in 2015, and a
fourth incident in September, according to the safety recall report. After meeting with US regulators in October, the firm decided to issue a voluntary recall.
</ellipsis>
</a-card>
</div>
</template>
<script>
// @ is an alias to /src
import Trend from '@/components/Trend'
import AvatarList from '@/components/AvatarList'
import CountDown from '@/components/CountDown/CountDown'
import Ellipsis from '@/components/Ellipsis'
const AvatarListItem = AvatarList.AvatarItem
export default {
name: 'Home',
components: {
Ellipsis,
CountDown,
Trend,
AvatarList,
AvatarListItem
},
data () {
return {
targetTime: new Date().getTime() + 3900000
}
},
methods: {
onEndHandle () {
this.$message.success('CountDown callback!!!')
},
onEndHandle2 () {
this.$notification.open({
message: 'Notification Title',
description: 'This is the content of the notification. This is the content of the notification. This is the content of the notification.'
})
}
}
}
</script>
<style scoped>
.home {
width: 900px;
margin: 0 auto;
padding: 25px 0;
}
.home > .banner {
text-align: center;
padding: 25px 0;
margin: 25px 0;
}
</style>
+285
View File
@@ -0,0 +1,285 @@
<template>
<div class="page-header-index-wide page-header-wrapper-grid-content-main">
<a-row :gutter="24">
<a-col :md="24" :lg="7">
<a-card :bordered="false">
<div class="account-center-avatarHolder">
<div class="avatar">
<img :src="getAvatar()" />
</div>
<div class="username">{{ nickname() }}</div>
<div class="bio">海纳百川有容乃大</div>
</div>
<div class="account-center-detail">
<p>
<i class="title"></i>交互专家
</p>
<p>
<i class="group"></i>蚂蚁金服某某某事业群某某平台部某某技术部UED
</p>
<p>
<i class="address"></i><span>浙江省</span><span>杭州市</span>
</p>
</div>
<a-divider />
<div class="account-center-tags">
<div class="tagsTitle">标签</div>
<div>
<template v-for="(tag, index) in tags">
<a-tooltip v-if="tag.length > 20" :key="tag" :title="tag">
<a-tag :key="tag" :closable="index !== 0" :afterClose="() => handleTagClose(tag)">
{{ `${tag.slice(0, 20)}...` }}
</a-tag>
</a-tooltip>
<a-tag v-else :key="tag" :closable="index !== 0" :afterClose="() => handleTagClose(tag)">{{ tag }}</a-tag>
</template>
<a-input
v-if="tagInputVisible"
ref="tagInput"
type="text"
size="small"
:style="{ width: '78px' }"
:value="tagInputValue"
@change="handleInputChange"
@blur="handleTagInputConfirm"
@keyup.enter="handleTagInputConfirm"
/>
<a-tag v-else @click="showTagInput" style="background: #fff; border-style: dashed;">
<a-icon type="plus" />
New Tag
</a-tag>
</div>
</div>
<a-divider :dashed="true" />
<div class="account-center-team">
<div class="teamTitle">团队</div>
<a-spin :spinning="teamSpinning">
<div class="members">
<a-row>
<a-col :span="12" v-for="(item, index) in teams" :key="index">
<a>
<a-avatar size="small" :src="item.avatar" />
<span class="member">{{ item.name }}</span>
</a>
</a-col>
</a-row>
</div>
</a-spin>
</div>
</a-card>
</a-col>
<a-col :md="24" :lg="17">
<a-card
style="width:100%"
:bordered="false"
:tabList="tabListNoTitle"
:activeTabKey="noTitleKey"
@tabChange="key => handleTabChange(key, 'noTitleKey')"
>
<article-page v-if="noTitleKey === 'article'"></article-page>
<app-page v-else-if="noTitleKey === 'app'"></app-page>
<project-page v-else-if="noTitleKey === 'project'"></project-page>
</a-card>
</a-col>
</a-row>
</div>
</template>
<script>
import { AppPage, ArticlePage, ProjectPage } from './page'
import { mapGetters } from 'vuex'
import { getFileAccessHttpUrl } from '@/api/manage'
export default {
components: {
AppPage,
ArticlePage,
ProjectPage
},
data () {
return {
tags: ['很有想法的', '专注设计', '辣~', '大长腿', '川妹子', '海纳百川'],
tagInputVisible: false,
tagInputValue: '',
teams: [],
teamSpinning: true,
tabListNoTitle: [{
key: 'article',
tab: '文章(8)'
}, {
key: 'app',
tab: '应用(8)'
}, {
key: 'project',
tab: '项目(8)'
}
],
noTitleKey: 'app'
}
},
mounted () {
this.getTeams()
},
methods: {
...mapGetters(['nickname', 'avatar']),
getAvatar () {
return getFileAccessHttpUrl(this.avatar())
},
getTeams () {
this.$http.get('/mock/api/workplace/teams')
.then(res => {
this.teams = res.result
this.teamSpinning = false
})
},
handleTabChange (key, type) {
this[type] = key
},
handleTagClose (removeTag) {
this.tags = this.tags.filter(tag => tag + '' !== removeTag + '')
},
showTagInput () {
this.tagInputVisible = true
this.$nextTick(() => {
this.$refs.tagInput.focus()
})
},
handleInputChange (e) {
this.tagInputValue = e.target.value
},
handleTagInputConfirm () {
const inputValue = this.tagInputValue
let tags = this.tags
if (inputValue && tags.indexOf(inputValue) === -1) {
tags = [...tags, inputValue]
}
Object.assign(this, {
tags,
tagInputVisible: false,
tagInputValue: ''
})
}
}
}
</script>
<style lang="less" scoped>
.page-header-wrapper-grid-content-main {
width: 100%;
height: 100%;
min-height: 100%;
transition: .3s;
.account-center-avatarHolder {
text-align: center;
margin-bottom: 24px;
& > .avatar {
width: 104px;
height: 104px;
margin: 0 auto 20px;
border-radius: 50%;
overflow: hidden;
img {
height: 100%;
width: 100%;
}
}
.username {
color: rgba(0, 0, 0, 0.85);
font-size: 20px;
line-height: 28px;
font-weight: 500;
margin-bottom: 4px;
}
}
.account-center-detail {
p {
margin-bottom: 8px;
padding-left: 26px;
position: relative;
}
i {
position: absolute;
height: 14px;
width: 14px;
left: 0;
top: 4px;
background: url(https://gw.alipayobjects.com/zos/rmsportal/pBjWzVAHnOOtAUvZmZfy.svg)
}
.title {
background-position: 0 0;
}
.group {
background-position: 0 -22px;
}
.address {
background-position: 0 -44px;
}
}
.account-center-tags {
.ant-tag {
margin-bottom: 8px;
}
}
.account-center-team {
.members {
a {
display: block;
margin: 12px 0;
line-height: 24px;
height: 24px;
.member {
font-size: 14px;
color: rgba(0, 0, 0, .65);
line-height: 24px;
max-width: 100px;
vertical-align: top;
margin-left: 12px;
transition: all 0.3s;
display: inline-block;
}
&:hover {
span {
color: #1890ff;
}
}
}
}
}
.tagsTitle, .teamTitle {
font-weight: 500;
color: rgba(0, 0, 0, .85);
margin-bottom: 12px;
}
}
</style>
+113
View File
@@ -0,0 +1,113 @@
<template>
<div class="app-list">
<a-list
:grid="{ gutter: 24, lg: 3, md: 2, sm: 1, xs: 1 }"
:dataSource="dataSource">
<a-list-item slot="renderItem" slot-scope="item">
<a-card :hoverable="true">
<a-card-meta>
<div style="margin-bottom: 3px" slot="title">{{ item.title }}</div>
<a-avatar class="card-avatar" slot="avatar" :src="item.avatar" size="small"/>
<div class="meta-cardInfo" slot="description">
<div>
<p>活跃用户</p>
<p>
<span>{{ item.activeUser }}<span></span></span>
</p>
</div>
<div>
<p>新增用户</p>
<p>{{ item.newUser | NumberFormat }}</p>
</div>
</div>
</a-card-meta>
<template class="ant-card-actions" slot="actions">
<a>
<a-icon type="download"/>
</a>
<a>
<a-icon type="edit"/>
</a>
<a>
<a-icon type="share-alt"/>
</a>
<a>
<a-dropdown>
<a class="ant-dropdown-link" href="javascript:">
<a-icon type="ellipsis"/>
</a>
<a-menu slot="overlay">
<a-menu-item>
<a href="javascript:">1st menu item</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:">2nd menu item</a>
</a-menu-item>
<a-menu-item>
<a href="javascript:">3rd menu item</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</a>
</template>
</a-card>
</a-list-item>
</a-list>
</div>
</template>
<script>
const dataSource = []
for (let i = 0; i < 11; i++) {
dataSource.push({
title: 'Alipay',
avatar: 'https://gw.alipayobjects.com/zos/rmsportal/WdGqmHpayyMjiEhcKoVE.png',
activeUser: 17,
newUser: 1700
})
}
export default {
name: 'Article',
components: {},
data () {
return {
dataSource
}
}
}
</script>
<style lang="less" scoped>
.app-list {
.meta-cardInfo {
zoom: 1;
margin-top: 16px;
> div {
position: relative;
text-align: left;
float: left;
width: 50%;
p {
line-height: 32px;
font-size: 24px;
margin: 0;
&:first-child {
color: rgba(0, 0, 0, .45);
font-size: 12px;
line-height: 20px;
margin-bottom: 4px;
}
}
}
}
}
</style>
+24
View File
@@ -0,0 +1,24 @@
<template>
<a-list>
<a-list-item>
</a-list-item>
</a-list>
</template>
<script>
import AList from 'ant-design-vue/es/list'
import AListItem from 'ant-design-vue/es/list/Item'
export default {
name: 'Article',
components: {
AList,
AListItem
}
}
</script>
<style scoped>
</style>
+17
View File
@@ -0,0 +1,17 @@
<template>
<a-list>
<a-list-item>
</a-list-item>
</a-list>
</template>
<script>
export default {
name: 'Project'
}
</script>
<style scoped>
</style>
+5
View File
@@ -0,0 +1,5 @@
import AppPage from './App'
import ArticlePage from './Article'
import ProjectPage from './Project'
export { AppPage, ArticlePage, ProjectPage }
+101
View File
@@ -0,0 +1,101 @@
<template>
<a-modal :visible="visible" title="修改头像" :maskClosable="false" :confirmLoading="confirmLoading" :width="800" @cancel="cancelHandel">
<a-row>
<a-col :xs="24" :md="12" :style="{height: '350px'}">
<vue-cropper
ref="cropper"
:img="options.img"
:info="true"
:autoCrop="options.autoCrop"
:autoCropWidth="options.autoCropWidth"
:autoCropHeight="options.autoCropHeight"
:fixedBox="options.fixedBox"
@realTime="realTime"
>
</vue-cropper>
</a-col>
<a-col :xs="24" :md="12" :style="{height: '350px'}">
<div class="avatar-upload-preview">
<img :src="previews.url" :style="previews.img" alt=""/>
</div>
</a-col>
</a-row>
<template slot="footer">
<a-button key="back" @click="cancelHandel">取消</a-button>
<a-button key="submit" type="primary" :loading="confirmLoading" @click="okHandel">保存</a-button>
</template>
</a-modal>
</template>
<script>
import { VueCropper } from 'vue-cropper'
export default {
components: {
VueCropper
},
data () {
return {
visible: false,
id: null,
confirmLoading: false,
options: {
img: '/avatar2.jpg',
autoCrop: true,
autoCropWidth: 200,
autoCropHeight: 200,
fixedBox: true
},
previews: {}
}
},
methods: {
edit (id) {
this.visible = true
this.id = id
/* 获取原始头像 */
},
close () {
this.id = null
this.visible = false
},
cancelHandel () {
this.close()
},
okHandel () {
const vm = this
vm.confirmLoading = true
setTimeout(() => {
vm.confirmLoading = false
vm.close()
vm.$message.success('上传头像成功')
}, 2000)
},
realTime (data) {
this.previews = data
}
}
}
</script>
<style lang="less" scoped>
.avatar-upload-preview {
position: absolute;
top: 50%;
transform: translate(50%, -50%);
width: 180px;
height: 180px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
}
</style>
+161
View File
@@ -0,0 +1,161 @@
<template>
<div class="account-settings-info-view">
<a-row :gutter="16">
<a-col :md="24" :lg="16">
<a-form layout="vertical">
<a-form-item
label="昵称"
>
<a-input placeholder="给自己起个名字" />
</a-form-item>
<a-form-item
label="Bio"
>
<a-textarea rows="4" placeholder="You are not alone."/>
</a-form-item>
<a-form-item
label="电子邮件"
:required="false"
>
<a-input placeholder="exp@admin.com"/>
</a-form-item>
<a-form-item
label="加密方式"
:required="false"
>
<a-select defaultValue="aes-256-cfb">
<a-select-option value="aes-256-cfb">aes-256-cfb</a-select-option>
<a-select-option value="aes-128-cfb">aes-128-cfb</a-select-option>
<a-select-option value="chacha20">chacha20</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="连接密码"
:required="false"
>
<a-input placeholder="h3gSbecd"/>
</a-form-item>
<a-form-item
label="登录密码"
:required="false"
>
<a-input placeholder="密码"/>
</a-form-item>
<a-form-item>
<a-button type="primary">提交</a-button>
<a-button style="margin-left: 8px">保存</a-button>
</a-form-item>
</a-form>
</a-col>
<a-col :md="24" :lg="8" :style="{ minHeight: '180px' }">
<div class="ant-upload-preview" @click="$refs.modal.edit(1)" >
<a-icon type="cloud-upload-o" class="upload-icon"/>
<div class="mask">
<a-icon type="plus" />
</div>
<img :src="option.img" alt=""/>
</div>
</a-col>
</a-row>
<avatar-modal ref="modal">
</avatar-modal>
</div>
</template>
<script>
import AvatarModal from './AvatarModal'
export default {
components: {
AvatarModal
},
data () {
return {
// cropper
preview: {},
option: {
img: '/avatar2.jpg',
info: true,
size: 1,
outputType: 'jpeg',
canScale: false,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 180,
autoCropHeight: 180,
fixedBox: true,
// 开启宽度和高度比例
fixed: true,
fixedNumber: [1, 1]
}
}
},
methods: {
}
}
</script>
<style lang="less" scoped>
.avatar-upload-wrapper {
height: 200px;
width: 100%;
}
.ant-upload-preview {
position: relative;
margin: 0 auto;
width: 100%;
max-width: 180px;
border-radius: 50%;
box-shadow: 0 0 4px #ccc;
.upload-icon {
position: absolute;
top: 0;
right: 10px;
font-size: 1.4rem;
padding: 0.5rem;
background: rgba(222, 221, 221, 0.7);
border-radius: 50%;
border: 1px solid rgba(0, 0, 0, 0.2);
}
.mask {
opacity: 0;
position: absolute;
background: rgba(0,0,0,0.4);
cursor: pointer;
transition: opacity 0.4s;
&:hover {
opacity: 1;
}
i {
font-size: 2rem;
position: absolute;
top: 50%;
left: 50%;
margin-left: -1rem;
margin-top: -1rem;
color: #d6d6d6;
}
}
img, .mask {
width: 100%;
max-width: 180px;
height: 100%;
border-radius: 50%;
overflow: hidden;
}
}
</style>
+25
View File
@@ -0,0 +1,25 @@
<template>
<a-list
itemLayout="horizontal"
:dataSource="data"
>
</a-list>
</template>
<script>
export default {
data () {
return {
data: []
}
},
methods: {
}
}
</script>
<style scoped>
</style>
+75
View File
@@ -0,0 +1,75 @@
<script>
import { colorList } from '@/components/tools/setting'
import ASwitch from 'ant-design-vue/es/switch'
import AList from 'ant-design-vue/es/list'
import AListItem from 'ant-design-vue/es/list/Item'
import { mixin } from '@/utils/mixin.js'
const Meta = AListItem.Meta
export default {
components: {
AListItem,
AList,
ASwitch,
Meta
},
mixins: [mixin],
data () {
return {
}
},
filters: {
themeFilter (theme) {
const themeMap = {
dark: '暗色',
light: '白色'
}
return themeMap[theme]
}
},
methods: {
colorFilter (color) {
const c = colorList.filter(o => o.color === color)[0]
return c && c.key
},
onChange (checked) {
if (checked) {
this.$store.dispatch('ToggleTheme', 'dark')
} else {
this.$store.dispatch('ToggleTheme', 'light')
}
}
},
render () {
return (
<AList itemLayout="horizontal">
<AListItem>
<Meta>
<a slot="title">风格配色</a>
<span slot="description">
整体风格配色设置
</span>
</Meta>
<div slot="actions">
<ASwitch checkedChildren="暗色" unCheckedChildren="白色" defaultChecked={this.navTheme === 'dark'} onChange={this.onChange} />
</div>
</AListItem>
<AListItem>
<Meta>
<a slot="title">主题色</a>
<span slot="description">
页面风格配色 <a domPropsInnerHTML={ this.colorFilter(this.primaryColor) }/>
</span>
</Meta>
</AListItem>
</AList>
)
}
}
</script>
<style scoped>
</style>
+206
View File
@@ -0,0 +1,206 @@
<template>
<div class="page-header-index-wide">
<a-card :bordered="false" :bodyStyle="{ padding: '16px 0', height: '100%' }" :style="{ height: '100%' }">
<div class="account-settings-info-main" :class="device" :style=" 'min-height:'+ mainInfoHeight ">
<div class="account-settings-info-left">
<a-menu
:mode="device === 'mobile' ? 'horizontal' : 'inline'"
:default-selected-keys="['settings']"
:style="{ border: '0', width: device === 'mobile' ? '560px' : 'auto'}"
type="inner"
@openChange="onOpenChange"
>
<a-menu-item key="settings">
<a @click="settingsClick()">
基本设置
</a>
</a-menu-item>
<a-menu-item key="security">
<a @click="securityClick()">安全设置</a>
</a-menu-item>
<a-menu-item key="custom">
<a @click="customClick()"> 个性化</a>
</a-menu-item>
<a-menu-item key="binding">
<a @click="bindingClick()">账户绑定</a>
</a-menu-item>
<a-menu-item key="notification">
<a @click="notificationClick()">新消息通知</a>
</a-menu-item>
</a-menu>
</div>
<div class="account-settings-info-right">
<div class="account-settings-info-title">
<span>{{ title }}</span>
</div>
<security ref="security" v-if="security"></security>
<base-setting ref="baseSetting" v-if="baseSetting"></base-setting>
<custom ref="custom" v-if="custom"></custom>
<notification ref="notification" v-if="notification"></notification>
<binding ref="binding" v-if="binding"></binding>
</div>
</div>
</a-card>
</div>
</template>
<script>
import { mixinDevice } from '@/utils/mixin.js'
import security from './Security'
import baseSetting from './BaseSetting'
import custom from './Custom'
import notification from './Notification'
import binding from './Binding'
export default {
components: {
security,
baseSetting,
custom,
notification,
binding
},
mixins: [mixinDevice],
data () {
return {
// horizontal inline
mode: 'inline',
mainInfoHeight: '100%',
openKeys: [],
defaultSelectedKeys: [],
// cropper
preview: {},
option: {
img: '/avatar2.jpg',
info: true,
size: 1,
outputType: 'jpeg',
canScale: false,
autoCrop: true,
// 只有自动截图开启 宽度高度才生效
autoCropWidth: 180,
autoCropHeight: 180,
fixedBox: true,
// 开启宽度和高度比例
fixed: true,
fixedNumber: [1, 1]
},
pageTitle: '',
title: '基本设置',
security: false,
baseSetting: true,
custom: false,
notification: false,
binding: false
}
},
created () {
this.updateMenu()
},
mounted () {
this.mainInfoHeight = (window.innerHeight - 285) + 'px'
},
methods: {
onOpenChange (openKeys) {
this.openKeys = openKeys
},
updateMenu () {
const routes = this.$route.matched.concat()
this.defaultSelectedKeys = [routes.pop().path]
},
// update-begin--Author:wangshuai Date:20200729 for:聚合路由错误 issues#1441--------------------
settingsClick () {
this.security = false
this.custom = false
this.notification = false
this.binding = false
this.baseSetting = true
this.title = '基本设置'
},
securityClick () {
this.baseSetting = false
this.custom = false
this.notification = false
this.binding = false
this.security = true
this.title = '安全设置'
},
notificationClick () {
this.security = false
this.custom = false
this.baseSetting = false
this.binding = false
this.notification = true
this.title = '新消息通知'
},
bindingClick () {
this.security = false
this.baseSetting = false
this.notification = false
this.custom = false
this.binding = true
this.title = '账号绑定'
},
customClick () {
this.security = false
this.baseSetting = false
this.notification = false
this.binding = false
this.custom = true
this.title = '个性化'
}
// update-end--Author:wangshuai Date:20200729 for:聚合路由错误 issues#1441--------------------
}
}
</script>
<style lang="less" scoped>
.account-settings-info-main {
width: 100%;
display: flex;
height: 100%;
overflow: auto;
&.mobile {
display: block;
.account-settings-info-left {
border-right: unset;
border-bottom: 1px solid #e8e8e8;
width: 100%;
height: 50px;
overflow-x: auto;
overflow-y: scroll;
}
.account-settings-info-right {
padding: 20px 40px;
}
}
.account-settings-info-left {
border-right: 1px solid #e8e8e8;
width: 224px;
}
.account-settings-info-right {
flex: 1 1;
padding: 8px 40px;
.account-settings-info-title {
color: rgba(0, 0, 0, .85);
font-size: 20px;
font-weight: 500;
line-height: 28px;
margin-bottom: 12px;
}
.account-settings-info-view {
padding-top: 12px;
}
}
}
</style>
@@ -0,0 +1,25 @@
<template>
<a-list
itemLayout="horizontal"
:dataSource="data"
>
</a-list>
</template>
<script>
export default {
data () {
return {
data: []
}
},
methods: {
}
}
</script>
<style scoped>
</style>
+41
View File
@@ -0,0 +1,41 @@
<template>
<a-list
itemLayout="horizontal"
:dataSource="data"
>
<a-list-item slot="renderItem" slot-scope="item, index" :key="index">
<a-list-item-meta>
<a slot="title">{{ item.title }}</a>
<span slot="description">
<span class="security-list-description">{{ item.description }}</span>
<span v-if="item.value"> : </span>
<span class="security-list-value">{{ item.value }}</span>
</span>
</a-list-item-meta>
<template v-if="item.actions">
<a slot="actions" @click="item.actions.callback">{{ item.actions.title }}</a>
</template>
</a-list-item>
</a-list>
</template>
<script>
export default {
data () {
return {
data: [
{ title: '账户密码', description: '当前密码强度', value: '强', actions: { title: '修改', callback: () => { this.$message.info('This is a normal message') } } },
{ title: '密保手机', description: '已绑定手机', value: '138****8293', actions: { title: '修改', callback: () => { this.$message.success('This is a message of success') } } },
{ title: '密保问题', description: '未设置密保问题,密保问题可有效保护账户安全', value: '', actions: { title: '设置', callback: () => { this.$message.warning('This is a message of error') } } },
{ title: '备用邮箱', description: '已绑定邮箱', value: 'ant***sign.com', actions: { title: '修改', callback: () => { this.$message.warning('This is message of warning') } } },
{ title: 'MFA 设备', description: '未绑定 MFA 设备,绑定后,可以进行二次确认', value: '', actions: { title: '绑定', callback: () => { this.$message.info('This is a normal message') } } }
]
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,342 @@
<template>
<internal-detail-page :title="$route.meta.title" :loading="loading">
<template v-slot:titleRightCustom>
<a @click="handleToggle" style="margin-left: 8px">
{{ toggleStatus ? $t('putAway') : $t('open') }}
<a-icon :type="toggleStatus ? 'up' : 'down'" />
</a>
</template>
<div class="detail-page-part-form" v-show="toggleStatus">
<!--标准名称-->
<div class="detail-page-part-form-item">
<label>{{ $t('standardName') }}</label>
<span class="can-click-table-text" @click="toDetailPage">{{ detailFormData.standardNameNumber }}</span>
</div>
<!--附件-->
<div class="detail-page-part-form-item">
<label>{{ $t('businessSupport.questionAnswer.attach') }}</label>
<file-echo :operate-fixed-right="false" :file-ids="detailFormData.standardTextFileId" :can-download="false" />
</div>
<!--流程稿件-->
<div class="detail-page-part-form-item">
<label>{{ $t('businessSupport.askForAdvice.processManuscript') }}</label>
<file-echo :operate-fixed-right="false" :file-ids="detailFormData.relatedFile" :can-download="false" />
</div>
<!--征求意见周期-->
<div class="detail-page-part-form-item">
<label>{{ $t('businessSupport.askForAdvice.consultationCycle') }}</label>
<span>{{ detailFormData.consultationPeriod }}</span>
</div>
</div>
<div class="table-operator">
<!--导出-->
<a-button icon="upload"
type="primary"
ghost
v-has="'askForAdvice:detail:export'"
@click="handleExportXls($t('businessSupport.askForAdvice.detailsOfComments'), '.xlsx')">
{{ $t('export') }}
</a-button>
</div>
<div>
<j-table
ref="table"
size="middle"
:columns="columns"
:data-source="dataSource"
rowKey="id"
:can-drag="true"
:loading="loading"
:pagination="ipagination"
:scroll="{x: '100%'}"
@change="handleTableChange">
<!--修改前修改后修改理由弹框显示内容-->
<template v-slot:modalDetail="{text, record}">
<div class="table-text" @click="showContent(text)" v-if="text">{{ text }}</div>
<div v-else>{{ global.emptyLine }}</div>
</template>
<template v-slot:file="{text}">
<file-echo :file-ids="text" :operate-fixed-right="false" :can-download="false" v-if="text" />
<div v-else>{{ global.emptyLine }}</div>
</template>
</j-table>
</div>
<text-content-modal ref="textContentModal" />
</internal-detail-page>
</template>
<script>
import InternalDetailPage from '../../../components/InternalDetailPage'
import FileEcho from '../../../components/FileEcho'
import { downFile } from '../../../api/manage'
import JTable from '../../../components/jero/JTable'
import { JeroListMixin } from '../../../mixins/JeroListMixin'
import { queryAskForAdviceDetail } from '../../../api/businessSupport'
import { StandardSource } from '../../../enums/commonEnums'
import TextContentModal from './modules/TextContentModal'
export default {
name: 'AskForAdviceDetail',
components: { JTable, FileEcho, InternalDetailPage, TextContentModal },
mixins: [JeroListMixin],
data () {
return {
toggleStatus: true,
disableMixinCreated: true,
loading: false,
columns: [
{
title: this.$t('serialNumber'),
dataIndex: '',
key: 'rowIndex',
align: 'center',
width: 80,
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{ // 条款号
title: this.$t('workCenter.enStandardRevision.clauseNumber'),
width: 180,
align: 'center',
dataIndex: 'chapterNumber',
scopedSlots: { customRender: 'text' }
},
{ // 条款名称
title: this.$t('docTool.split.clauseName'),
width: 180,
align: 'center',
dataIndex: 'chapterName',
scopedSlots: { customRender: 'text' }
},
{ // 修改意见内容
title: this.$t('workCenter.standardConsultationProcess.reasonForRevision'),
children: [
{ // 修改前
title: this.$t('workCenter.enStandardChange.beforeUpdate'),
align: 'center',
width: 180,
dataIndex: 'modificationBefore',
scopedSlots: { customRender: 'modalDetail' }
},
{ // 修改后
title: this.$t('workCenter.enStandardChange.afterUpdate'),
align: 'center',
width: 180,
dataIndex: 'modificationAfter',
scopedSlots: { customRender: 'modalDetail' }
},
{ // 修改理由
title: this.$t('workCenter.enStandardChange.updateReason'),
align: 'center',
width: 180,
dataIndex: 'modificationReason',
scopedSlots: { customRender: 'modalDetail' }
}
]
},
{ // 提出部门
title: this.$t('workCenter.enStandardChange.proposingDepartment'),
align: 'center',
width: 180,
dataIndex: 'depart',
scopedSlots: { customRender: 'text' }
},
{ // 提出人
title: this.$t('workCenter.enStandardChange.introducer'),
align: 'center',
width: 180,
dataIndex: 'designEngineerName',
scopedSlots: { customRender: 'text' }
},
{ // 处理意见
title: this.$t('workCenter.enStandardRevision.handlingSuggestion'),
align: 'center',
width: 180,
dataIndex: 'isAdopt_dictText'
},
// 依据描述
{
dataIndex: 'basisDescription',
title: this.$t('projectLibrary.specialProjectLibrary.basisDescription'),
width: 180
},
// 佐证材料
{
dataIndex: 'basisFile',
title: this.$t('projectLibrary.specialProjectLibrary.supportingMaterial'),
width: 180,
scopedSlots: { customRender: 'file' }
}
],
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
detailFormData: {} // 上方表单的数据
}
},
created () {
this.initDetailData()
},
methods: {
initDetailData () {
this.loading = true
const params = {
pageNo: this.ipagination.current,
pageSize: this.ipagination.pageSize,
businessId: this.$route.query.id
}
queryAskForAdviceDetail(params).then(res => {
if (res.success) {
// 处理表格数据
const tableResult = res.result.processFbConsultationList
this.dataSource = tableResult.records || []
if (tableResult.total) {
this.ipagination.total = tableResult.total
} else {
this.ipagination.total = 0
}
// 处理表单数据
this.detailFormData = res.result.processFbStandardConsultation || {}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleToggle () {
this.toggleStatus = !this.toggleStatus
},
handleExportXls (fileName, fileSuffix = '.xlsx') {
if (!fileName || typeof fileName !== 'string') {
fileName = '导出文件'
}
const param = this.getQueryParams()
param.businessId = this.$route.query.id
if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
param.selections = this.selectedRowKeys.join(',')
}
console.log('导出参数', param)
// 加一个大的提示
const modalLoading = this.$info({
title: '提示',
content: <span>正在导出请稍候 <a-spin size="small" /></span>,
keyboard: false
})
// 把知道了这个按钮去掉
this.$nextTick(() => {
document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none'
})
downFile('/laws/consultation/exportExcel', param).then((data) => {
if (!data) {
this.$message.warning('文件下载失败')
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName + fileSuffix)
} else {
const url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' }))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + fileSuffix)
console.log(fileName + fileSuffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) // 下载完成移除元素
window.URL.revokeObjectURL(url) // 释放掉blob对象
}
}).finally(() => {
// 销毁这个提示
modalLoading.destroy()
})
},
handleTableChange (pagination, filters, sorter) {
// 分页、排序、筛选变化时触发
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.order ? sorter.field : 'createTime'
this.isorter.order = sorter.order === 'ascend' ? 'asc' : 'desc'
}
this.ipagination = pagination
this.initDetailData()
},
/**
* 跳转标准详情
*/
toDetailPage () {
const source = this.detailFormData.source
const standardId = this.detailFormData.standardId
let path
// 根据来源跳转不同的详情页
switch (source) {
// 国内
case StandardSource.DOMESTIC.value:
path = '/standardRegulationLibrary/DomesticStandardDetail'
break
// 海外
case StandardSource.OVERSEAS.value:
path = '/standardRegulationLibrary/OverseasStandardDetail'
break
// 企标
case StandardSource.ENTERPRISE.value:
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
break
default:
break
}
if (!path) {
return
}
const query = {
id: standardId
}
this.$openPageNewSheet({
path, query
})
},
showContent (text) {
this.$refs.textContentModal.open(text)
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.detail-page-part-form {
padding: 0;
}
.detail-page-part-form-item {
width: 100%;
label {
width: 6.5rem;
}
/deep/ .file-info {
font-size: 16px;
}
}
.can-click-table-text {
color: @primary-color !important;
}
</style>
@@ -0,0 +1,134 @@
<template>
<a-card :bordered="false">
<!--查询区域-->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<!--标准名称/编号-->
<a-col :md="6" :sm="12">
<a-form-item :label="$t('businessSupport.askForAdvice.standardNameNumber')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.askForAdvice.standardNameNumber')"
v-model="queryParam.standardNameNumber" />
</a-form-item>
</a-col>
<!--征求意见周期-->
<a-col :md="6" :sm="12">
<a-form-item :label="$t('businessSupport.askForAdvice.consultationCycle')">
<a-range-picker
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style='margin-left: -1px'
v-model="queryParam.consultationPeriodList"
:disabled="false" />
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search" style="margin-left: 8px" v-has="'askForAdvice:search'">
{{ $t('query') }}
</a-button>
<a-button type="primary" @click="searchReset" icon="reload" ghost>{{ $t('reset') }}</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<div>
<j-table
ref="table"
size="middle"
:columns="columns"
:data-source="dataSource"
rowKey="id"
:can-drag="true"
:loading="loading"
:pagination="ipagination"
:scroll="{x: '100%'}"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
@change="handleTableChange">
<!--跳转详情-->
<template v-slot:detail="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text can-click-table-text" v-if="text || text === 0" @click="toDetailPage(record)">{{ text }}</div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</j-table>
</div>
</a-card>
</template>
<script>
import { JeroListMixin } from '@/mixins/JeroListMixin'
import JTable from '../../../components/jero/JTable'
import { filterObj } from '../../../utils/util'
import { askForAdvice } from '../../../common/lang/businessSupport/zh'
export default {
name: 'AskForAdviceList',
computed: {
askForAdvice () {
return askForAdvice
}
},
components: { JTable },
mixins: [JeroListMixin],
data () {
return {
columns: [
// 标准名称/编号
{
title: this.$t('businessSupport.askForAdvice.standardNameNumber'),
width: 200,
dataIndex: 'standardNameNumber',
scopedSlots: { customRender: 'detail' }
},
// 征求意见周期
{
title: this.$t('businessSupport.askForAdvice.consultationCycle'),
width: 200,
dataIndex: 'consultationPeriod'
}
],
dataSource: [],
url: {
list: '/laws/consultation/queryByPage'
}
}
},
methods: {
toDetailPage ({ id }) {
this.$router.push({
path: '/businessSupport/AskForAdviceDetail',
query: { id }
})
},
getQueryParams () {
// 获取查询条件
const sqp = {}
if (this.superQueryParams) {
sqp.superQueryParams = encodeURI(this.superQueryParams)
sqp.superQueryMatchType = this.superQueryMatchType
}
const param = Object.assign(sqp, this.queryParam, this.isorter, this.filters)
param.field = this.getQueryField()
param.pageNo = this.ipagination.current
param.pageSize = this.ipagination.pageSize
if (param.consultationPeriodList && param.consultationPeriodList.length > 0) {
param.consultationPeriod = param.consultationPeriodList.join(',')
delete param.consultationPeriodList
}
return filterObj(param)
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
</style>
@@ -0,0 +1,42 @@
<template>
<j-modal
:title="$t('view')"
:visible="visible"
:width="1000"
:footer="false"
switchFullscreen
@cancel="handleCancel">
<div class="text-content">{{ content }}</div>
</j-modal>
</template>
<script>
export default {
name: 'TextContentModal',
data () {
return {
visible: false,
content: null
}
},
methods: {
open (text) {
this.content = text
this.visible = true
},
handleCancel () {
this.visible = false
this.content = null
}
}
}
</script>
<style scoped lang="less">
.text-content {
max-height: calc(100vh - 200px - 55px);
overflow: auto;
margin: 0 -24px;
padding: 0 24px;
}
</style>
@@ -0,0 +1,688 @@
<template>
<a-card :bordered="false">
<div class="left-tree-div">
<div class="div-left">
<div>
<a-input-search style="margin-bottom: 8px" :placeholder="$t('nodeQuickLookup')" @change="onLeftChange"
@search="onSearch"/>
<a-tree
:selected-keys="selectedKeys"
:expanded-keys.sync="expandedKeys"
:auto-expand-parent="autoExpandParent"
:defaultExpandAll="true"
:tree-data="gData"
@expand="onExpand"
@rightClick="rightClick"
@select="onSelect"
>
<template #title="{ key: treeKey, title }">
<a-dropdown :trigger="['contextmenu']">
<!-- <span>{{ title }}</span>-->
<span v-if="title.indexOf(searchValue) >= -1 && title.length <= 18" :title="title"
class="expand-title">
{{ title.substr(0, title.indexOf(searchValue)) }}<span
style="color: #f50">{{ searchValue }}</span>{{ title.substr(title.indexOf(searchValue) + searchValue.length) }}
</span>
<span
v-else-if="title.indexOf(searchValue) >= -1 && title.length > 18 && title.indexOf(searchValue)>18"
:title="title">
{{title && title.length > 18?title.slice(0,17)+'...':title}}
</span>
<span
v-else-if="title.indexOf(searchValue) >= -1 && title.length > 18 && title.indexOf(searchValue)<=18"
:title="title">
{{ title.substr(0, title.indexOf(searchValue)) }}<span style="color: #f50">{{ title.indexOf(searchValue)+searchValue.length>18?searchValue.substr(0,18-title.indexOf(searchValue)) : searchValue }}</span>{{ title.indexOf(searchValue)+searchValue.length>18?'...': title.substr(title.indexOf(searchValue) + searchValue.length,18-title.indexOf(searchValue) - searchValue.length)+'...' }}
</span>
<span v-else :title="title">
{{title && title.length > 18?title.slice(0,17)+'...':title}}
</span>
<template #overlay v-if='manager'>
<a-menu>
<a-menu-item key="1" @click="orgAdd">{{$t('treeRight.addFolder')}}</a-menu-item>
<a-menu-item key="4" @click="orgAdds" v-if="notAddChildNode">{{$t('treeRight.addSubFolders')}}</a-menu-item>
<a-menu-item key="2" @click="orgEdit">{{$t('treeRight.editFolder')}}</a-menu-item>
<a-menu-item key="3" @click="orgDelete">{{$t('treeRight.deleteFolders')}}</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</a-tree>
</div>
</div>
<div class="div-right">
<div class="div-right-wrapper">
<div class="right-search-header table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="8" :sm="8">
<a-form-item :label="$t('fileName')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('fileName')"
v-model="queryParam.fileName"></a-input>
</a-form-item>
</a-col>
<a-col :md="8" :sm="8">
<a-form-item :label="$t('uploadedBy')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('uploadedBy')"
v-model="queryParam.createBy"></a-input>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="8" :sm="8">
<a-form-item :label="$t('uploadTime')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style='margin-left: -1px'
v-model="queryParam.time"
:disabled="false"/>
</a-form-item>
</a-col>
</template>
<span style="float: right;overflow: hidden;margin-right: 11px" class="table-page-search-submitButtons">
<a-col :md="6" :sm="24">
<a @click="handleToggleSearch">
<span class="icon-open">{{ !toggleSearchStatus ? $t('open') : $t('putAway') }}</span>
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
<a-button class="box-button" style="margin-left: 8px"
@click="searchReset">{{ $t('reset') }}</a-button>
<a-button class="box-button" style="margin-left: 8px" type="primary" @click="searchQuery">{{ $t('query') }}</a-button>
</a-col>
</span>
</a-row>
</a-form>
</div>
<div class="table-operator" style="overflow:hidden;margin-bottom: 20px">
<div style="float: right;margin-bottom: 0px;margin-left: 20px">
<!-- v-has="'externalReport:upload'"-->
<div class="operator-text"
@click="handleuploadFile" v-if='authmanage'>
<a-icon type="cloud-upload"/>
{{ $t('uploadFileBtn') }}
</div>
<div class="operator-text"
v-has="'externalReport:batchDelete'"
@click="handleDel" v-if='authmanage'>
<a-icon type="delete"/>
{{ $t('batchDelete') }}
</div>
</div>
</div>
<div>
<j-table
ref="table"
size="middle"
:can-drag="true"
:loading="loading"
:pagination="ipagination"
rowKey="id"
:scroll="{x: '100%',y:'calc(100vh - 180px)'}"
:data-source="dataSource"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:columns="columns"
@change="handleTableChange"
>
<template v-slot:projectName="{text}" :title="text">
{{ text }}
</template>
<template v-slot:fileName="{text}" :title="text">
{{ text }}
</template>
<!-- v-has="'externalReport:download'"-->
<!-- v-has="'externalReport:edit'"-->
<!-- v-has="'externalReport:delete'"-->
<!-- v-if="authmanage"-->
<!-- <template v-slot:operation="{text, record}">-->
<!-- <a class="text-operation"-->
<!-- @click="download(record)">{{$t('download')}}</a>-->
<!-- <a-divider type="vertical"/>-->
<!-- <a-dropdown v-if="authmanage">-->
<!-- <a class="ant-dropdown-link">-->
<!-- {{ $t('more') }} <a-icon type="down"/>-->
<!-- </a>-->
<!-- <a-menu slot="overlay">-->
<!-- <a-menu-item>-->
<!-- <a class="text-operation"-->
<!-- @click="handleEdit(record)">{{$t('edit')}}</a>-->
<!-- </a-menu-item>-->
<!-- <a-menu-item>-->
<!-- <a class="text-operation"-->
<!-- @click="batchDel(record)">{{$t('delete')}}</a>-->
<!-- </a-menu-item>-->
<!-- </a-menu>-->
<!-- </a-dropdown>-->
<!-- </template>-->
<template v-slot:operation="{text, record}">
<a v-for="(operation, index) in operationList.slice(0, operateMaxNum)" :key="index" class="text-operation"
@click="operationClick(record)">{{operation.text}}</a>
<!-- authmanage &&-->
<a-divider v-if="operationList.length >= operateMaxNum" type="vertical"/>
<a-dropdown v-if="operationList.length >= operateMaxNum">
<a class="ant-dropdown-link">{{ $t('more') }} <a-icon type="down"/></a>
<a-menu slot="overlay">
<a-menu-item v-for="(operation, index) in operationList.slice(operateMaxNum)" :key="index">
<!-- v-if="authmanage"-->
<a class="text-operation" @click="operationClick(record)">{{operation.text}}</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</j-table>
</div>
<!-- 上传文件-->
<addModel ref="addModelRef" @addModelList="addModelList"/>
<!-- 添加节点-->
<add-node-modal ref="addNodeModal" @ok="addNodeOk"></add-node-modal>
</div>
</div>
</div>
</a-card>
</template>
<script>
import JTable from '@/components/jero/JTable'
// 上传文件弹框
import AddModel from './modules/AddModel'
// 添加节点弹框
import AddNodeModal from './modules/AddNodeModal'
import { getAction, postAction, downloadFile, deleteAction } from '@/api/manage'
import { JeroListMixin } from '@/mixins/JeroListMixin'
export default {
name: 'DataCenter',
components: {
JTable,
AddModel,
AddNodeModal
},
mixins: [JeroListMixin],
data () {
return {
operationList: [
{
text: this.$t('download'),
clickEvent: 'download',
has: 'externalReport:download'
},
{
text: this.$t('edit'),
clickEvent: 'handleEdit',
has: 'externalReport:edit'
},
{
text: this.$t('delete'),
clickEvent: 'batchDel',
has: 'externalReport:delete'
}
],
labelCol: { span: 6 },
wrapperCol: { span: 16 },
disabled: false,
manager: false, // 是否有树右键菜单
addSub: true,
dataSource: [
{ id: 1 }
],
expandedKeys: [],
searchValue: '',
autoExpandParent: true,
dataList: [],
gData: [],
loading: false,
attId: '',
data: [],
nodeTreeItem: null, // 右键菜单
tmpStyle: '', // 右键菜单位置
infoId: '',
menuId: '',
itemId: '',
itemVal: {},
columns: [
{
title: this.$t('fileName'),
align: 'left',
ellipsis: true,
dataIndex: 'fileName',
scopedSlots: { customRender: 'fileName' },
width: 270
},
{
title: this.$t('fileDeclaration'),
align: 'left',
ellipsis: true,
dataIndex: 'fileDescription',
scopedSlots: { customRender: 'projectName' },
width: 390
},
{
title: this.$t('uploadedBy'),
align: 'left',
ellipsis: true,
dataIndex: 'createBy',
width: 150
},
{
title: this.$t('uploadTime'),
align: 'left',
ellipsis: true,
dataIndex: 'createTime',
width: 150
},
{
title: this.$t('operation'),
align: 'center',
fixed: 'right',
ellipsis: true,
width: 240,
scopedSlots: { customRender: 'operation' }
}
],
url: {
list: '/extRepo/extRepoData/page',
getSysCategoryTree: '/sys/category/getSysCategoryTree'
},
// upAccept:'.zip', //导入文件类型
selectedKeys: [],
notAddChildNode: false,
authmanage: false,
fullWidth: ''
}
},
mounted () {
this.loadMenuData()
this.generateList(this.gData)
},
methods: {
// 获取左侧菜单的下级
generateList (data) {
for (let i = 0; i < data.length; i++) {
const node = data[i]
const key = node.key
this.dataList.push({ key, title: key })
if (node.children) {
this.generateList(node.children)
}
}
},
// 获取传入的树中最上层的key
getParentKey (key, tree) {
let parentKey
for (let i = 0; i < tree.length; i++) {
const node = tree[i]
if (node.children) {
if (node.children.some(item => item.key === key)) {
parentKey = node.key
} else if (this.getParentKey(key, node.children)) {
parentKey = this.getParentKey(key, node.children)
}
}
}
return parentKey
},
// 树的展开
onExpand (expandedKeys) {
this.expandedKeys = expandedKeys
this.autoExpandParent = false
},
// 左侧搜索框的改变
onLeftChange (e) {
const value = e.target.value
const expandedKeys = this.dataList
.map(item => {
if (item.title.indexOf(value) > -1) {
return this.getParentKey(item.key, this.gData)
}
return null
})
.filter((item, i, self) => item && self.indexOf(item) === i)
Object.assign(this, {
expandedKeys,
searchValue: value,
autoExpandParent: true
})
this.onSearch()
},
// 获取左侧目录数据
loadMenuData () {
// let params = {
// infoId: this.infoId
// }
this.loading = true
getAction(`extRepo/extRepoFolder/list`, {}).then(res => {
if (res.success) {
this.data = res.result.list
this.menuId = this.data[0].key
this.loadData()
this.gData = [...this.data]
Object.assign(this, {
expandedKeys: this.expandedKeys,
autoExpandParent: true
})
}
}).finally(() => {
this.loading = false
})
},
// 鼠标右键
rightClick ({ event, node }) {
this.notAddChildNode = node._props.dataRef.level !== 4
this.manager = node._props.dataRef.manager === true
const x = event.currentTarget.offsetLeft + event.currentTarget.clientWidth
const y = event.currentTarget.offsetTop
console.log(node._props.dataRef)
this.nodeTreeItem = {
pageX: x,
pageY: y,
authManageIdsName: node._props.dataRef.authManageIdsName,
authViewIds: node._props.dataRef.authViewIds,
key: node._props.dataRef.key,
authManageIds: node._props.dataRef.authManageIds,
authViewIdsName: node._props.dataRef.authViewIdsName,
title: node._props.dataRef.title,
folderName: node._props.dataRef.folderName,
orderId: node._props.dataRef.orderId
}
this.nodeItem = JSON.parse(JSON.stringify(this.nodeTreeItem))
this.tmpStyle = {
position: 'absolute',
maxHeight: 40,
textAlign: 'center',
left: `${x + 10 - 0}px`,
top: `${y + 6 - 0}px`,
background: `#FFFFFF`,
zIndex: `2`
}
},
// 用于点击空白处隐藏增删改菜单
clearMenu () {
this.nodeTreeItem = null
},
// 选择树节点
onSelect (selectedKeys) {
this.menuId = selectedKeys[0]
this.selectedKeys = selectedKeys
this.searchQuery()
},
// 树形新增文件夹
orgAdd () {
this.$refs.addNodeModal.menuTitle = this.$t('treeRight.addFolder')
const form = {}
form.supFolder = this.nodeItem.key
form.addSub = false
const params = {}
params.id = this.nodeItem.key
postAction(`extRepo/extRepoFolder/isAllowSiblingFolder`, params).then(res => {
if (res.success) {
this.$refs.addNodeModal.open(false, form)
} else {
this.$message.warning(res.message)
}
})
},
// 树形新增子文件夹
orgAdds () {
this.$refs.addNodeModal.menuTitle = this.$t('treeRight.addSubFolders')
const form = {}
form.supFolder = this.nodeItem.key
form.addSub = true
this.$refs.addNodeModal.open(false, form)
},
// 树形修改
orgEdit () {
this.menuTitle = this.$t('treeRight.editFolder')
const form = {}
form.id = this.nodeItem.key
form.authManageIds = this.nodeItem.authManageIds
form.authViewIds = this.nodeItem.authViewIds
form.authManageIdsName = this.nodeItem.authManageIdsName
form.authViewIdsName = this.nodeItem.authViewIdsName
form.folderName = this.nodeItem.folderName
form.orderId = this.nodeItem.orderId
this.$refs.addNodeModal.open(true, form)
},
// 树形删除
orgDelete () {
this.$confirm({
content: this.$t('treeRight.deleteNode'),
onOk:
async () => {
deleteAction(`extRepo/extRepoFolder/delete`, { id: this.nodeItem.key }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loadMenuData()
this.searchQuery()
} else {
this.$message.warning(res.message)
}
})
}
})
},
// 添加或编辑节点成功
addNodeOk () {
this.expandedKeys.push(this.nodeItem.id)
this.loadMenuData()
this.searchQuery()
},
// 上传文件弹框成功后的回调
addModelList () {
this.ipagination.current = 1
this.loadData()
},
// 获取列表数据
loadData (item) {
this.queryParam.supFolder = this.menuId
this.queryParam.pageNo = this.ipagination.current
this.queryParam.pageSize = this.ipagination.pageSize
const params = {
...this.queryParam,
...item
}
this.loading = true
getAction(this.url.list, params).then(res => {
if (res.success) {
if (res.result.pageList.current > 1 && res.result.pageList.records.length === 0) {
this.ipagination.current = 1
this.loadData()
return
}
this.dataSource = res.result.pageList.records || []
this.authmanage = res.result.auth_manage
this.ipagination.total = res.result.pageList.total
}
}).finally(() => {
this.loading = false
})
},
// 搜索
searchQuery (item) {
this.ipagination.current = 1
if (this.queryParam.time && this.queryParam.time instanceof Array) {
this.queryParam.time = this.queryParam.time.join(',')
}
this.loadData(item)
},
// 清空
searchReset () {
this.ipagination.current = 1
this.queryParam = {}
this.loadData()
},
// 编辑
handleEdit (item) {
this.$refs.addModelRef.editModel(JSON.parse(JSON.stringify(item)))
},
// 下载
download (item) {
const query = {
id: item.fileKey
}
downloadFile('/sys/common/downLoadFile', item.fileName, query)
},
// 删除
batchDel (val) {
this.$confirm({
content: this.$t('confirmDeletion'),
onOk: () => {
deleteAction('extRepo/extRepoData/delete', { id: val.id }).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.loadData()
} else {
this.$message.warning(res.message)
}
})
}
})
},
// 批量删除
handleDel () {
if (this.selectedRowKeys.length > 0) {
this.$confirm({
content: this.$t('confirmBatchDeletion'),
onOk: () => {
const idList = this.selectedRowKeys
deleteAction('extRepo/extRepoData/deleteBatch', { ids: idList.join(',') }).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.selectedRowKeys = []
this.loadData()
} else {
this.$message.warning(res.message)
}
})
}
})
} else {
this.$message.warning(this.$t('selectLeastOne'))
}
},
// 选择框
onSelectChange (selectedRowKeys, rows) {
this.selectedRowKeys = selectedRowKeys
this.selectedRows = rows
},
// 上传文件
handleuploadFile (e) {
if (this.menuId) {
this.$refs.addModelRef.addModel(this.menuId)
} else {
this.$message.warning(this.$t('businessSupp.dataCenter.selectDirectorylocation'))
}
},
onSearch () {
const self = this
const tree = JSON.parse(JSON.stringify(this.data))
this.expandedKeys = []
if (this.searchValue.trim() === '') {
this.gData = JSON.parse(JSON.stringify(this.data))
return
}
if (tree && tree.length > 0) {
tree.forEach((n, i, a) => {
self.searchEach(n, this.searchValue)
})
// 没有叶子节点的根节点也要清理掉
const length = tree.length
for (let i = length - 1; i >= 0; i--) {
const e2 = tree[i]
if (!this.isHasChildren(e2) && e2.title.indexOf(this.searchValue) <= -1) {
tree.splice(i, 1)
this.expandedKeys.push(e2.key)
}
}
this.gData = [...tree]
}
},
searchEach (node, value) {
const depth = this.getTreeDepth(node)
const self = this
for (let i = 0; i < depth - 1; i++) {
let spliceCounter = 0
this.traverseTree(node, n => {
if (self.isHasChildren(n)) {
const children = n.children
const length = children.length
for (let j = length - 1; j >= 0; j--) {
const e3 = children[j]
if (!self.isHasChildren(e3) && e3.title.indexOf(value) <= -1) {
children.splice(j, 1)
spliceCounter++
} else {
this.expandedKeys.push(e3.key)
}
}
}
})
if (spliceCounter === 0) {
break
}
}
},
// 判断树形结构中的一个节点是否具有孩子节点
isHasChildren (node) {
let flag = false
if (node.children && node.children.length > 0) {
flag = true
}
return flag
},
// 通过传入根节点获得树的深度
getTreeDepth (node) {
if (undefined === node || node == null) {
return 0
}
let r = 0
let currentLevelNodes = [node]
while (currentLevelNodes.length > 0) {
r++
let nextLevelNodes = []
for (let i = 0; i < currentLevelNodes.length; i++) {
const e = currentLevelNodes[i]
if (this.isHasChildren(e)) {
nextLevelNodes = nextLevelNodes.concat(e.children)
}
}
currentLevelNodes = nextLevelNodes
}
return r
},
traverseTree (node, callback) {
if (!node) {
return
}
const stack = []
stack.push(node)
let tmpNode
while (stack.length > 0) {
tmpNode = stack.pop()
callback(tmpNode)
if (tmpNode.children && tmpNode.children.length > 0) {
for (let i = tmpNode.children.length - 1; i >= 0; i--) {
stack.push(tmpNode.children[i])
}
}
}
},
// 正则替换小数点
limitNumber (value) {
if (typeof value === 'string') {
return !isNaN(Number(value)) ? value.replace(/\./g, '') : 0
} else if (typeof value === 'number') {
return !isNaN(value) ? String(value).replace(/\./g, '') : 0
} else {
return 0
}
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
</style>
@@ -0,0 +1,183 @@
<template>
<div>
<j-modal
:title="title"
:maskClosable="false"
:width="600"
placement="right"
:closable="true"
@cancel="handleCancel"
:visible="visible"
switchFullscreen
style="height: 100%;overflow: auto;padding-bottom: 53px;">
<a-spin :tip="this.$t('businessSupp.dataCenter.fileUploaded')" :spinning="spinning">
<a-form>
<a-form-model :model="formInline" class="form-add" :rules="rules" ref="ruleForm">
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="required">*</span>
<span class="title-text-text"
:title="$t('fileName')">{{$t('fileName')}}</span>
</div>
<a-form-model-item class="item-model" prop="fileKey">
<a-button type="primary" class="button-text"
@click="clickButtonToUpload('fileKey')">
{{ (formInline.fileKey === 'null' || formInline.fileKey === '' ||
formInline.fileKey == null) ? $t('uploadFile.clickUpload') : $t('uploadFile.viewUploadedFiles')
}}
</a-button>
</a-form-model-item>
</div>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="title-text-text" :title="$t('fileDeclaration')">{{$t('fileDeclaration')}}</span>
</div>
<a-form-model-item class="item-model-textarea" prop="fileDescription">
<a-textarea :placeholder="$t('pleaseEnter')+$t('fileDeclaration')"
:maxLength="200"
:rows="4"
v-model="formInline.fileDescription"/>
</a-form-model-item>
</div>
</a-col>
</a-row>
</a-form-model>
</a-form>
</a-spin>
<div class="drawer-bootom-button">
<a-button style="margin-right: 8px" @click="handleCancel">{{$t('cancel')}}</a-button>
<a-button @click="handleSubmit" type="primary">{{$t('submit')}}</a-button>
</div>
</j-modal>
<upload-file ref="uploadFile" @change="uploadFileChange" :return-url="false"></upload-file>
</div>
</template>
<script>
import UploadFile from '@/components/UploadFile'
import { postAction, putAction } from '@/api/manage'
export default {
name: 'AddModel',
components: { UploadFile },
data () {
return {
formInline: {},
spinning: false,
menuId: '',
visible: false,
url: {
add: 'extRepo/extRepoData/add',
edit: 'extRepo/extRepoData/edit'
},
rules: {
fileDescription: [
// { required: true, message: this.$t('pleaseEnter')+this.$t('fileDeclaration'), trigger: 'blur' },
],
fileKey: [
{
required: true,
message: this.$t('fileName') + this.$t('cannotEmpty'),
trigger: 'change'
}
]
},
disabled: false,
title: ''
}
},
methods: {
addModel (menuId) {
this.visible = true
this.title = this.$t('uploadFileBtn')
this.disabled = false
this.menuId = menuId
this.formInline = {}
this.$nextTick(() => {
this.$refs.ruleForm.clearValidate()
})
},
editModel (value) {
this.visible = true
this.title = this.$t('edit')
this.disabled = true
this.$nextTick(() => {
this.formInline = value
this.$refs.ruleForm.clearValidate()
})
},
handleCancel () {
if (this.spinning === true) {
this.visible = true
} else {
this.$refs.ruleForm.clearValidate()
this.visible = false
}
},
handleSubmit () {
if (this.spinning) {
return
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.spinning = true
let url = ''
let Action
if (this.formInline.id) {
url = this.url.edit
Action = putAction
} else {
url = this.url.add
Action = postAction
}
this.formInline.supFolder = this.menuId
const query = JSON.parse(JSON.stringify(this.formInline))
Object.keys(query).forEach(res => {
if (query[res] && query[res] instanceof Array) {
query[res] = query[res].join(',')
}
})
Action(url, query).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.visible = false
this.$emit('addModelList')
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.spinning = false
})
}
})
},
// 文件上传改变后的回调
uploadFileChange (data) {
const attIdList = []
if (data && data.length > 0) {
data.map(item => {
attIdList.push(item.id || data.name)
})
}
/** 赋值给当前对应的表单文件 */
this.formInline[this.uploadName] = attIdList.join(',')
this.formInline = { ...this.formInline }
this.$refs.ruleForm.clearValidate(['fileKey'])
},
clickButtonToUpload (item) {
this.$refs.uploadFile.open(this.formInline[item])
this.uploadName = item
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
</style>
@@ -0,0 +1,210 @@
<template>
<j-modal
class="add-menus"
:title="title"
:width="600"
:visible="visible"
switchFullscreen
:confirm-loading="confirmLoading"
@cancel="handleCancelMenuRight"
@ok="hideModalMenuRight"
>
<a-form-model
class="split-click-right-form"
ref="ruleForm"
:model="form"
:rules="rules"
>
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="required">*</span>
<span class="title-text-text"
:title="$t('treeRight.folderName')">{{$t('treeRight.folderName')}}</span>
</div>
<a-form-model-item prop="folderName" style='width: 72%'>
<a-input
v-model="form.folderName"
style='width: 130%'
class="box-input add-input"
maxLength="100"
:placeholder="$t('pleaseEnter')+$t('treeRight.folderName')"/>
</a-form-model-item>
</div>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="title-text-text"
:title="$t('treeRight.administrativePrivileges')">{{$t('treeRight.administrativePrivileges')}}</span>
</div>
<a-form-model-item class="itemModel-multi" prop="authManageIdsName">
<a-select
mode="multiple"
v-model="form.authManageIdsName"
:placeholder="$t('pleaseSelect')+$t('treeRight.administrativePrivileges')"
:filter-option="false"
:not-found-content="null"
allowClear
@search="personSearch"
>
<a-select-option v-for="item in personList" :key="item.id" :value="item.id">
{{ item.realname }}
</a-select-option>
<template slot="notFoundContent">
<a-empty></a-empty>
</template>
</a-select>
</a-form-model-item>
</div>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="title-text-text"
:title="$t('treeRight.checkPermissions')">{{$t('treeRight.checkPermissions')}}</span>
</div>
<a-form-model-item class="itemModel-multi" prop="authViewIdsName">
<a-select
mode="multiple"
v-model="form.authViewIds"
:placeholder="$t('pleaseSelect')+$t('treeRight.administrativePrivileges')"
:filter-option="false"
:not-found-content="null"
allowClear
@search="personSearch"
>
<a-select-option v-for="item in personList" :key="item.id" :value="item.id">
{{ item.realname }}
</a-select-option>
<template slot="notFoundContent">
<a-empty></a-empty>
</template>
</a-select>
</a-form-model-item>
</div>
</a-col>
</a-row>
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="Required">*</span>
<span class="title-text-text"
:title="$t('treeRight.folderOrder')">{{$t('treeRight.folderOrder')}}</span>
</div>
<a-form-model-item class="itemModel" prop="orderId">
<a-input-number v-model="form.orderId" :min="1" :max="9999" class="box-input add-input"
:placeholder="$t('pleaseEnter')+$t('treeRight.folderOrder')" :formatter="limitNumber"
:parser="limitNumber"/>
</a-form-model-item>
</div>
</a-col>
</a-row>
</a-form-model>
</j-modal>
</template>
<script>
import { getUserList } from '@/api/api'
import { postAction, putAction } from '@/api/manage'
export default {
name: 'AddNodeModal',
data () {
return {
title: this.$t('add'),
visible: false,
confirmLoading: false,
form: {},
rules: {
folderName: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('treeRight.folderName'), trigger: 'blur' }
],
// authManageIdsName: [
// { required: true, message: this.$t('pleaseSelect') + this.$t('treeRight.administrativePrivileges'), trigger: 'change' },
// ],
// authViewIdsName: [
// { required: true, message: this.$t('pleaseSelect') + this.$t('treeRight.checkPermissions'), trigger: 'change' },
// ],
orderId: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('treeRight.folderOrder'), trigger: 'blur' }
]
},
personList: [],
isEdit: false
}
},
methods: {
open (isEdit, form) {
this.visible = true
this.isEdit = true
this.form = Object.assign({}, form)
},
personSearch (value) {
getUserList({ pageNo: 1, pageSize: 20, realname: '*' + value + '*' }).then(res => {
if (res.success) {
this.personList = res.result.records || []
}
})
},
// 取消文件夹的新增或编辑
handleCancelMenuRight () {
this.visible = false
this.$refs.ruleForm.clearValidate()
},
// 增加文件夹确定
hideModalMenuRight () {
if (this.confirmLoading) {
return
}
if (!this.isEdit) {
const params = {
...this.form
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.confirmLoading = true
postAction(`extRepo/extRepoFolder/add`, params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.form = {}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
this.visible = false
})
}
})
} else {
const params = {
...this.form
}
putAction(`extRepo/extRepoFolder/edit`, params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.form = {}
this.loadMenuData()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
this.visible = false
})
}
},
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,289 @@
<template>
<a-card :bordered="false">
<!-- 搜索区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<!-- 分类 -->
<a-col :span="6">
<a-form-item :label="$t('businessSupport.questionAnswer.classify')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-dict-select-tag
:placeholder="$t('pleaseSelect')+$t('businessSupport.questionAnswer.classify')"
dict-code="problem_library_type"
v-model="queryParam.type">
</j-dict-select-tag>
</a-form-item>
</a-col>
<!-- 标准号 -->
<a-col :span="6">
<a-form-item :label="$t('standardNumber')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-input :placeholder="$t('pleaseEnter')+$t('standardNumber')" v-model="queryParam.standardNumber"></j-input>
</a-form-item>
</a-col>
<!-- 标准名称 -->
<a-col :span="6">
<a-form-item :label="$t('standardName')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-input :placeholder="$t('pleaseEnter')+$t('standardName')" v-model="queryParam.standardName"></j-input>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<!-- 标题 -->
<a-col :span="6">
<a-form-item :label="$t('title')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-input :placeholder="$t('pleaseEnter')+$t('title')" v-model="queryParam.title"></j-input>
</a-form-item>
</a-col>
</template>
<div style="float: right;overflow: hidden;" class="table-page-search-submitButtons">
<a @click="handleToggleSearch">
{{ toggleSearchStatus ? $t('putAway') : $t('open') }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
<a-button type="primary" @click="searchQuery" icon="search" style="margin-left: 8px" v-has="'businessSupport:questionAnswer:search'">{{ $t('query') }}</a-button>
<a-button type="primary" ghost @click="searchReset" icon="reload" style="margin-left: 8px">{{ $t('reset') }}</a-button>
</div>
</a-row>
</a-form>
</div>
<!-- 操作区域 -->
<div class="table-operator">
<!-- 提问 -->
<a-button icon="plus" type="primary" v-has="'businessSupport:questionAnswer:quiz'"
@click="handleQuiz">
{{ $t('businessSupport.questionAnswer.quiz') }}
</a-button>
</div>
<div>
<j-table
:can-drag="true"
:scroll="{x: '100%', y: yScrollHeight}"
ref="table"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<!-- 解决columns中dataIndex是title的时候控制台报错的问题 -->
<template v-slot:titleSlot="{record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ record.title || record.title === 0 ? record.title : global.emptyLine }}</template>
<div class="table-text">{{ record.title || record.title === 0 ? record.title : global.emptyLine }}</div>
</a-tooltip>
</template>
<!-- 附件 -->
<template v-slot:attach="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a v-if="text" class="link-a" @click="handleAttachClick(record)">{{ text }}</a>
<span v-else>{{ global.emptyLine }}</span>
</a-tooltip>
</template>
<!-- 操作栏 -->
<div slot="action" slot-scope="{record}" class="action-span-cell">
<!-- 是自己的提问 -->
<template v-if="record.userId === userInfo.id">
<a @click="handleDetail(record)"
class="table-ope-btn"
v-has="'businessSupport:questionAnswer:detail'">
{{ $t('view') }}
</a>
<a-divider type="vertical" />
<a @click="handleDelete(record.id)"
class="table-ope-btn"
v-has="'businessSupport:questionAnswer:delete'">
{{ $t('delete') }}
</a>
</template>
<!-- 是别人的提问 -->
<template v-else>
<a @click="handleAnswer(record)"
class="table-ope-btn"
v-has="'businessSupport:questionAnswer:answer'">
{{ $t('businessSupport.questionAnswer.answer') }}
</a>
</template>
</div>
</j-table>
</div>
<quiz-modal ref="quizModal" @ok="modalFormOk"></quiz-modal>
</a-card>
</template>
<script>
import { JeroListMixin } from '@/mixins/JeroListMixin'
import JTable from '@/components/jero/JTable'
import Vue from 'vue'
import { ACCESS_TOKEN, USER_INFO } from '@/store/mutation-types'
import { previewPdf } from '@/utils/previewPdf'
import { getFileAccessHttpUrl, downloadFile } from '@/api/manage'
import QuizModal from './modules/QuizModal'
import { kkFilePreview } from '@/utils/kkFilePreview'
const FILE_TYPE_IMGS = ['jpg', 'jpeg', 'png', 'raw']
const FILE_TYPE_PDF = 'pdf'
// 支持预览的文件后缀
const CAN_PREVIEW_FILE_SUFFIX = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
export default {
name: 'QuestionAnswerList',
mixins: [JeroListMixin],
components: { QuizModal, JTable },
data () {
return {
/* 排序参数 */
isorter: {
column: 'quizTime',
order: 'desc'
},
labelCol: {
span: 4
},
wrapperCol: {
span: 14
},
columns: [
{ // 分类
title: this.$t('businessSupport.questionAnswer.classify'),
align: 'center',
width: 180,
dataIndex: 'type_dictText',
scopedSlots: { customRender: 'text' }
},
{ // 标准编号
title: this.$t('standardNumber'),
align: 'center',
width: 180,
dataIndex: 'standardNumber',
scopedSlots: { customRender: 'text' }
},
{ // 标准名称
title: this.$t('standardName'),
align: 'center',
width: 180,
dataIndex: 'standardName',
scopedSlots: { customRender: 'text' }
},
{ // 标题
title: this.$t('title'),
align: 'center',
width: 180,
scopedSlots: { customRender: 'titleSlot' }
},
{ // 项目
title: this.$t('project'),
align: 'center',
width: 180,
dataIndex: 'project_dictText',
scopedSlots: { customRender: 'text' }
},
{ // 问题描述
title: this.$t('businessSupport.questionAnswer.questionDesc'),
align: 'center',
width: 180,
dataIndex: 'problemDescription',
scopedSlots: { customRender: 'text' }
},
{ // 提问时间
title: this.$t('businessSupport.questionAnswer.quizTime'),
align: 'center',
width: 180,
dataIndex: 'quizTime',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('operation'),
scopedSlots: { customRender: 'action' },
align: 'center',
fixed: 'right',
width: 200
}
],
url: {
list: '/laws/library/lawsProblemLibrary/page',
delete: '/laws/library/lawsProblemLibrary/deleteProblem'
},
userInfo: {}
}
},
mounted () {
this.userInfo = Vue.ls.get(USER_INFO)
},
methods: {
// 提问
handleQuiz () {
this.$refs.quizModal.title = this.$t('businessSupport.questionAnswer.quiz')
this.$refs.quizModal.open()
},
// 点击相关材料
handleAttachClick (record) {
// 截取文件后缀名
const fileSuffix = record.declareFileName ? record.declareFileName.split('.')[record.declareFileName.split('.').length - 1] : ''
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${record.declareFile}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${record.declareFileName}`
const canPreview = CAN_PREVIEW_FILE_SUFFIX.some(tt => fileSuffix === tt)
// 判断是否为可预览格式的文件
if (!canPreview) {
this.$message.loading(this.$t('uploadFile.cannotPreview')).then(() => {
this.handleDownload(record.declareFile, record.declareFileName)
})
return
}
// 图片预览,使用自己添加的组件
if (FILE_TYPE_IMGS.includes(fileSuffix.toLowerCase())) {
this.imageUrl = getFileAccessHttpUrl(record.declareFile)
// 获取viewer实例
const viewer = this.$el.querySelector('.image').$viewer
// 调用show方法进行显示预览图
viewer.show()
// this.$refs.imagePreviewModal.open(file)
return
}
// pdf预览
if (FILE_TYPE_PDF.includes(fileSuffix.toLowerCase())) {
const url = previewPdf(record.declareFile)
window.open(url)
return
}
// 其余可预览文件仍使用KKFile进行预览
kkFilePreview(fileFullUrl)
},
handleDownload (id, name) {
// 下载文件
downloadFile(`/sys/common/download/${id}`, name)
},
// 查看
handleDetail (record) {
this.$router.push({
path: '/businessSupport/questionDetail',
query: {
id: record.id,
canAnswer: 0
}
})
},
// 回答
handleAnswer (record) {
this.$router.push({
path: '/businessSupport/questionDetail',
query: {
id: record.id,
canAnswer: 1
}
})
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
/deep/ .ant-form-item-label{
min-width: 70px !important;
}
</style>
@@ -0,0 +1,278 @@
<template>
<j-modal
:title="title"
:maskClosable="true"
:width="800"
:closable="true"
switchFullscreen
@cancel="handleCancel"
@ok="handleOk"
:visible="visible">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row :gutter="24">
<!-- 分类 -->
<a-col :span="12">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('businessSupport.questionAnswer.classify')">
<a-select :placeholder="$t('pleaseSelect') + $t('businessSupport.questionAnswer.classify')"
allowClear
@change="typeChange"
v-decorator.trim="[ 'type', validatorRules.type]">
<a-select-option :value="null" :key="-1">请选择</a-select-option>
<a-select-option value="1">国内标准</a-select-option>
<a-select-option value="2">海外标准</a-select-option>
<a-select-option value="3">企业标准</a-select-option>
<a-select-option value="4">其他问题</a-select-option>
</a-select>
</a-form-item>
</a-col>
<!-- 标准编号 -->
<a-col :span="12">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('standardNumber')">
<standard-selection
v-decorator.trim="[ 'standardNumber', validatorRules.standardNumber]"
:placeholder="$t('pleaseSelect') + $t('standardNumber')"
:source="['1', '2', '3'].includes(form.getFieldValue('type')) ? form.getFieldValue('type') : undefined"
:disabledSourceSearch="['1', '2', '3'].includes(form.getFieldValue('type'))"
:selectOnly="true"
type="radio"
@nameChange="standardNameChange"
/>
</a-form-item>
</a-col>
<!-- 标准名称 -->
<a-col :span="12">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('standardName')">
<a-input disabled :placeholder="$t('pleaseEnter') + $t('standardName')"
v-decorator.trim="[ 'standardName', validatorRules.standardName]"/>
</a-form-item>
</a-col>
<!-- 标题 -->
<a-col :span="12">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('title')">
<a-input v-decorator.trim="[ 'title', validatorRules.title]"
:maxLength="50"
:placeholder="$t('pleaseEnter') + $t('title')"/>
</a-form-item>
</a-col>
<!-- 项目 -->
<a-col :span="12">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('project')">
<a-select :placeholder="$t('pleaseSelect') + $t('project')"
allowClear
show-search
:filter-option="filterOption"
v-decorator="[ 'project', validatorRules.project]">
<a-select-option :value="undefined" :key="-1">请选择</a-select-option>
<a-select-option v-for="item in projectList" :value="item.id" :key="item.id" :title="item.projectName">{{ item.projectName }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<!-- 提问时间 -->
<a-col :span="12">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('businessSupport.questionAnswer.quizTime')">
<a-date-picker
style="width: 100%"
v-decorator.trim="[ 'quizTime', validatorRules.quizTime]"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
showTime
></a-date-picker>
</a-form-item>
</a-col>
<!-- 问题描述 -->
<a-col :span="24">
<a-form-item :labelCol="longLabelCol" :wrapperCol="longWrapperCol" :label="$t('businessSupport.questionAnswer.questionDesc')">
<a-textarea
v-decorator="[ 'problemDescription', validatorRules.problemDescription]"
:placeholder="$t('pleaseEnter')+$t('businessSupport.questionAnswer.questionDesc')"
:maxLength="500"
:rows="4" />
<j-upload v-decorator.trim="[ 'file', validatorRules.file]"
returnId
multiple
isDownload>
</j-upload>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-spin>
</j-modal>
</template>
<script>
import { postAction } from '@/api/manage'
import { getCarTypeProjectList } from '@/api/projectLibraryApi.js'
// 双向绑定的标准选择器
import StandardSelection from '@/components/selection/StandardSelection'
import pick from 'lodash.pick'
import moment from 'moment'
export default {
name: 'QuizModal',
components: { StandardSelection },
data () {
return {
title: '',
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 }
},
longLabelCol: {
xs: { span: 24 },
sm: { span: 3 }
},
longWrapperCol: {
xs: { span: 24 },
sm: { span: 21 }
},
validatorRules: {
type: {
rules: [
{ required: true, message: this.$t('pleaseSelect') + this.$t('businessSupport.questionAnswer.classify') }
],
validateTrigger: 'change'
},
standardNumber: {
rules: [
{ required: true, message: this.$t('pleaseSelect') + this.$t('standardNumber') }
],
validateTrigger: 'change'
},
standardName: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('standardName') }
],
validateTrigger: 'blur'
},
title: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('title') }
],
validateTrigger: 'blur'
},
project: {
rules: [
{ required: false, message: this.$t('pleaseSelect') + this.$t('project') }
],
validateTrigger: 'change'
},
quizTime: {
rules: [
{ required: false, message: this.$t('pleaseSelect') + this.$t('businessSupport.questionAnswer.quizTime') }
],
validateTrigger: 'change'
},
problemDescription: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('businessSupport.questionAnswer.questionDesc') }
],
validateTrigger: 'blur'
},
file: {
rules: [
{ required: false, message: this.$t('pleaseSelect') + this.$t('businessSupport.questionAnswer.attach') }
],
validateTrigger: 'change'
}
},
url: {
quiz: '/laws/library/lawsProblemLibrary/addProblem'
},
projectList: [] // 项目下拉框数据
}
},
created () {
this.getCarTypeProjectList()
},
methods: {
open () {
this.visible = true
this.form.resetFields()
const param = {}
param.quizTime = moment().format('YYYY-MM-DD HH:mm:ss')
this.$nextTick(() => {
this.form.setFieldsValue(pick(param, 'quizTime'))
})
},
// 获取项目下拉框数据
getCarTypeProjectList () {
getCarTypeProjectList({ column: 'createTime', order: 'desc' }).then(res => {
if (res.success) {
this.projectList = res.result
} else {
this.projectList = []
}
})
},
// 分类改变
typeChange (value) {
// 清空标准,否则分类可以和标准类型不一致
this.form.resetFields(['standardNumber', 'standardName'])
if (value) {
if (value === '4') {
// 是其他问题,标准编号和标准名称不必填
this.validatorRules.standardNumber.rules[0].required = false
this.validatorRules.standardName.rules[0].required = false
} else {
// 标准编号和标准名称必填
this.validatorRules.standardNumber.rules[0].required = true
this.validatorRules.standardName.rules[0].required = true
}
}
},
// 标准选择名称改变的回调
standardNameChange (value) {
const param = { standardName: value }
this.$nextTick(() => {
this.form.setFieldsValue(pick(param, 'standardName'))
})
},
handleCancel () {
this.close()
},
handleOk () {
if (this.confirmLoading) return
this.form.validateFields((err, values) => {
if (!err) {
this.confirmLoading = true
const param = Object.assign({}, values)
postAction(this.url.quiz, param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
close () {
this.visible = false
},
filterOption (input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,479 @@
<template>
<internal-detail-page :title="pageTitle" :loading="loading">
<div class="detail-page-title">{{ model.title }}</div>
<div class="detail-page-second-title">
{{ $t('businessSupport.questionAnswer.poster') + '' + (model.orgCode_dictText || '') + ' ' + (model.userId_dictText || '') }}
</div>
<div class="detail-page-part">
<div class="detail-page-part-form">
<!-- 标准号/标准名称 -->
<div class="detail-page-part-form-item-large">
<label>{{ $t('standardNumOrName') }}</label>
<span class="link" @click="clickStandard">{{ model.standardNumber && model.standardName ? model.standardNumber + ' ' + model.standardName : global.emptyLine }}</span>
</div>
<!-- 项目 -->
<div class="detail-page-part-form-item-large">
<label>{{ $t('project') }}</label>
<span>{{ model.project_dictText || global.emptyLine }}</span>
</div>
<!-- 附件 -->
<div class="detail-page-part-form-item-large-file">
<label>{{ $t('businessSupport.questionAnswer.attach') }}</label>
<template v-if="fileList && fileList.length > 0">
<div class="file-box">
<div class="file-list-file" v-for="file in fileList" :key="file.id">
<div class="file-list-file-info">
<a-icon type="link" />
<div class="file-list-file-name" @click="handlePreview(file)">{{ file.fileName }}</div>
</div>
<!-- 下载-->
<a-button type="link" icon="download" class="file-btn" @click="handleDownload(file)">
{{ $t('download') }}
</a-button>
<!-- 无水印下载-->
<!--<a-button type="link" v-has="btnPermission.unwatermarkedDownload" class="file-btn">-->
<!-- <i class="iconfont icon-water-drop-slash"></i>-->
<!-- {{ $t('unwatermarkedDownload') }}-->
<!--</a-button>-->
</div>
</div>
</template>
<span v-else>{{ global.emptyLine }}</span>
</div>
</div>
</div>
<!-- 问题描述部分 -->
<div class="question-desc-wrapper">
<p class="question-desc">{{ model.problemDescription }}</p>
<p class="right-bottom-p">
<span class="time-span">{{ model.quizTime }}</span>
<span class="right-bottom-p-span">{{ answerArr.length + $t('businessSupport.questionAnswer.answerNum')}}</span>
</p>
</div>
<!-- 历史回答数据 -->
<template v-if="answerArr && answerArr.length > 0">
<div v-for="(item, index) in answerArr" :key="index" class="answer-form-wrapper">
<div class="answer-form-wrapper-title">
<a-avatar shape="square" :size="28" :src="getAvatarView(item.avatar)" icon="user"/>
<p class="user-name">{{ item.departName || '' }} {{ item.name }}</p>
<p v-if="item.isTop" class="top-p">{{ $t('businessSupport.questionAnswer.top') }}</p>
</div>
<div class="answer-form-desc-div">{{ item.answerDescription }}</div>
<p class="operate-bottom-p">
<span>{{ item.createTime }}</span>
<!-- 置顶评论的操作按钮 -->
<template v-if="item.isTop">
<!-- 取消置顶 -->
<a class="operate-bottom-p-a" @click="setOrCancelTop(item.id)" v-has="'quiz:detail:topOrCancel'">{{ $t('businessSupport.questionAnswer.cancelTop') }}</a>
<!-- 删除 : 发帖人评论人有置顶权限的用户可以删除 ?? -->
<!--<a v-if="userInfo.id === model.userId || userInfo.id === item.userId || isHasPermission('quiz:detail:topOrCancel')" v-has="'quiz:detail:deleteAnswer'" class="operate-bottom-p-a" @click="deleteAnswer(item.id)">{{ $t('delete') }}</a>-->
</template>
<!-- 非置顶评论 -->
<template v-else>
<!-- 设置为置顶 -->
<a class="operate-bottom-p-a" @click="setOrCancelTop(item.id)" v-has="'quiz:detail:topOrCancel'">{{ $t('businessSupport.questionAnswer.setTop') }}</a>
<!-- 删除 : 发帖人评论人有置顶权限的用户可以删除,置顶的评论不允许删除取消置顶后允许删除 -->
<a v-if="userInfo.id === model.userId || userInfo.id === item.userId || isHasPermission('quiz:detail:topOrCancel')" v-has="'quiz:detail:deleteAnswer'" class="operate-bottom-p-a" @click="deleteAnswer(item.id)">{{ $t('delete') }}</a>
</template>
</p>
</div>
</template>
<!-- 回答区域 -->
<div v-if="canAnswer === 1" class="answer-form-wrapper">
<div class="answer-form-wrapper-title">
<a-avatar shape="square" :size="28" :src="getAvatarView(currentUser.avatar)" icon="user"/>
<p class="user-name">{{ currentUser.departIds_dicText || '' }} {{ currentUser.realname }}({{ currentUser.username }})</p>
<p class="answer_question">{{ $t('businessSupport.questionAnswer.answerQuestion') }}</p>
</div>
<a-textarea v-model="answerDesc" :autoSize="{minRows: 4}" :maxLength="2000"></a-textarea>
<p class="answer-desc-num">{{ answerDesc.length }}/2000{{ $t('businessSupport.questionAnswer.word') }}</p>
<a-button class="submit-btn" type="primary" @click="submit" v-has="'quiz:detail:answerSubmit'">{{ $t('submit') }}</a-button>
</div>
<!-- 图片预览容器 -->
<div id="images">
<div class="image" v-viewer="{movable: false}">
<img v-show="image" :src="imageUrl">
</div>
</div>
</internal-detail-page>
</template>
<script>
import { USER_INFO, ACCESS_TOKEN } from '@/store/mutation-types'
import Vue from 'vue'
import { getFileAccessHttpUrl, downloadFile } from '@/api/manage'
import { getQuizById, answerQuiz, setOrCancelTop, deleteAnswer } from '@/api/businessSupport'
import { getFileInfo } from '@/api/api'
import { previewPdf } from '@/utils/previewPdf'
import InternalDetailPage from '@/components/InternalDetailPage'
import { kkFilePreview } from '@/utils/kkFilePreview'
import { isHasPermission } from '@/utils/hasPermission'
// 支持预览的文件后缀
const CAN_PREVIEW_FILE_SUFFIX = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
const FILE_TYPE_IMGS = ['jpg', 'jpeg', 'png', 'raw']
const FILE_TYPE_PDF = 'pdf'
const Base64 = require('js-base64').Base64
export default {
name: 'AnswerPage',
components: { InternalDetailPage },
data () {
return {
loading: false,
pageTitle: '',
model: {},
answerArr: [],
fileList: [],
currentUser: {
avatar: '',
realname: '',
orgCodeTxt: ''
},
answerDesc: '',
canAnswer: false,
userInfo: {},
image: false,
imageUrl: ''
}
},
mounted () {
this.initData(this.$route.query.id)
const userInfo = Vue.ls.get(USER_INFO)
this.userInfo = userInfo
this.currentUser = Object.assign({}, userInfo)
this.canAnswer = Number(this.$route.query.canAnswer)
if (this.canAnswer === 1) {
this.pageTitle = this.$t('businessSupport.questionAnswer.answerQuestionTitle')
} else {
this.pageTitle = this.$t('businessSupport.questionAnswer.questionDetail')
}
},
methods: {
isHasPermission,
// 初始化获取数据信息
initData (id) {
this.loading = true
getQuizById({ id: id }).then(res => {
if (res.success) {
this.model = Object.assign({}, res.result)
if (this.model.file) {
const fileList = this.model.file.split(',')
const fileInfoList = []
fileList.forEach(async id => {
const fileInfo = await this.getFileInfo(id)
if (fileInfo) {
fileInfoList.push(fileInfo)
}
})
this.fileList = fileInfoList
}
this.answerArr = this.model.answerList
}
}).finally(() => {
this.loading = false
})
},
// 获取头像
getAvatarView (avatar) {
return getFileAccessHttpUrl(avatar)
},
getFileInfo (fileId) {
return new Promise(resolve => {
let fileInfo
getFileInfo({ id: fileId }).then(res => {
if (res.success) {
fileInfo = res.result
}
}).finally(() => {
resolve(fileInfo)
})
})
},
handlePreview (file) {
if (!file || !file.url) {
return
}
// 截取文件后缀名
const fileSuffix = file.fileName ? file.fileName.split('.')[file.fileName.split('.').length - 1] : ''
const canPreview = CAN_PREVIEW_FILE_SUFFIX.some(tt => fileSuffix.toLowerCase() === tt)
// 判断是否为可预览格式的文件
if (!canPreview) {
this.$message.loading('该文件类型不支持预览,正在为您准备下载...').then(() => {
this.handleDownload(file)
})
return
}
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.fileName}`
// 图片预览,使用自己添加的组件
if (canPreview && FILE_TYPE_IMGS.includes(fileSuffix.toLowerCase())) {
this.imageUrl = getFileAccessHttpUrl(file.id)
// 获取viewer实例
const viewer = this.$el.querySelector('.image').$viewer
// 调用show方法进行显示预览图
viewer.show()
// this.$refs.imagePreviewModal.open(file)
return
}
// pdf预览
if (canPreview && FILE_TYPE_PDF.includes(fileSuffix.toLowerCase())) {
const url = previewPdf(file.id)
console.log(url)
window.open(url)
return
}
// 其余可预览文件仍使用KKFile进行预览
kkFilePreview(fileFullUrl)
},
handleDownload (file) {
// 下载文件
downloadFile(`/laws/standard/common/downloadWithWaterMark/${file.id}`, file.fileName)
},
// 点击标准跳转详情
clickStandard () {
let path = ''
// 需要先判断是哪种标准
if (this.model.type === '1') {
path = '/standardRegulationLibrary/DomesticStandardDetail'
} else if (this.model.type === '2') {
path = '/standardRegulationLibrary/OverseasStandardDetail'
} else if (this.model.type === '3') {
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
}
this.$openPageNewSheet({
path: path,
query: {
id: this.model.standardId
}
})
},
submit () {
if (!this.answerDesc) {
this.$message.warning(this.$t('businessSupport.questionAnswer.pleaseEnterAnswerDesc'))
return
}
const param = {}
param.problemId = this.model.id
param.answerDescription = this.answerDesc
answerQuiz(param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.initData(this.model.id)
this.answerDesc = ''
} else {
this.$message.warning(res.message)
}
})
},
// 设置或取消置顶
setOrCancelTop (answerId) {
setOrCancelTop({ id: answerId }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.initData(this.model.id)
} else {
this.$message.warning(res.message)
}
})
},
// 删除回答
deleteAnswer (answerId) {
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
deleteAnswer({ id: answerId }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.initData(this.model.id)
} else {
this.$message.warning(res.message)
}
})
}
})
}
}
}
</script>
<style scoped lang="less">
// 二级标题
.detail-page-second-title {
font-size: 16px;
text-align: center;
font-weight: 550;
margin-bottom: 20px;
}
.detail-page-part-form-item-large {
width: 100%;
display: flex;
margin-bottom: 16px;
label {
width: 10em;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
font-weight: 400;
color: #86909C;
}
span {
white-space: pre-wrap;
word-break: break-all;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
color: #1D2129;
}
.link {
color: @primary-color;
cursor: pointer;
}
}
.detail-page-part-form-item-large-file {
width: 100%;
display: flex;
label {
width: 10em;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
font-weight: 400;
color: #86909C;
}
a {
white-space: pre-wrap;
word-break: break-all;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
}
}
// 问题描述部分
.question-desc-wrapper {
border-radius: 8px;
border: 1px solid #E5E6EB;
margin-bottom: 20px;
padding: 20px;
.question-desc {
font-size: 16px;
}
// 右下角的时间和按钮区域
.right-bottom-p {
height: 20px;
text-align: right;
margin-bottom: 0;
.time-span {
color: #86909C;
}
.right-bottom-p-span {
margin-left: 8px;
}
.right-bottom-p-a {
margin-left: 8px;
}
}
}
// 标准信息-附件的文件列表
.file-box {
flex: 1;
width: 0;
}
.file-list {
&-file {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
.file-btn {
padding-right: 0 !important;
}
}
&-file:last-child {
margin-bottom: 0;
}
&-file-info {
display: flex;
align-items: center;
color: @primary-color;
cursor: pointer;
flex: 1;
width: 0;
}
&-file-info > .anticon {
margin-right: 8px;
}
&-file-name {
white-space: nowrap;
overflow: hidden;
flex: 1;
width: 0;
text-overflow: ellipsis;
}
&-file-operate {
.ant-btn > .anticon + span, .ant-btn > .iconfont + span {
margin-left: 10px;
}
.ant-btn:first-child {
margin-left: 15px;
}
}
}
.answer-form-wrapper {
.answer-form-wrapper-title {
display: flex;
.user-name {
font-weight: 560;
margin: 0 10px;
font-size: 16px;
}
.answer_question {
font-size: 16px;
color: #86909C;
}
.top-p {
color: @primary-color;
margin-bottom: 0;
}
}
.answer-form-desc-div {
border-radius: 8px;
border: 1px solid #E5E6EB;
padding: 20px;
margin-top: 5px;
}
.answer-desc-num {
text-align: right;
margin-top: 5px;
margin-bottom: 0;
}
.submit-btn {
margin-top: 20px;
}
// 右下角的时间和按钮区域
.operate-bottom-p {
height: 20px;
text-align: right;
margin: 5px 0 10px 0;
.time-span {
color: #86909C;
}
.operate-bottom-p-span {
margin-left: 8px;
}
.operate-bottom-p-a {
margin-left: 8px;
}
}
}
</style>
@@ -0,0 +1,357 @@
<template>
<internal-detail-page custom-icon
:title="$t('businessSupport.standardizationActivity.currentNode')"
:sub-title="currentNodeName"
sub-title-tooltip
:loading="loading"
:content-padding="0">
<template v-slot:customIcon>
<i class="iconfont icon-nav_apartment page-icon" />
</template>
<template v-slot:titleRightCustom>
<!--新增-->
<a-button type="primary" ghost icon="plus" @click="handleAdd" v-has="'standardActivity:add'">{{
$t('newlyAdded')
}}
</a-button>
<!--编辑-->
<a-button type="primary" ghost icon="edit" @click="handleEdit" v-has="'standardActivity:edit'">{{ $t('edit') }}</a-button>
<!--查看-->
<a-button type="primary" ghost icon="eye" @click="handleDetail" v-has="'standardActivity:view'">{{ $t('view') }}</a-button>
<!--会议统计列表-->
<a-button type="primary" ghost icon="unordered-list" @click="handleStatistical" v-has="'standardActivity:statistical'">{{
$t('businessSupport.standardizationActivity.conferenceStatisticsList')
}}
</a-button>
</template>
<div class="table-page-search-wrapper search-box" :class="{'search-box-shadow': showSearch}">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :span="14">
<!-- 节点名称-->
<a-form-item :label="$t('businessSupport.standardizationActivity.nodeName')">
<a-input v-model="nodeName"
:placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.nodeName')"
@input="handleSearch" />
</a-form-item>
</a-col>
<a-col :span="10">
<div style="float: right;overflow: hidden;margin-right: 11px" class="table-page-search-submitButtons">
<a-button class="box-button"
style="margin-left: 8px"
type="primary"
@click="searchQuery"
icon="search"
v-has="'standardActivity:search'">{{
$t('query')
}}
</a-button>
<a-button class="box-button" icon="reload" @click="searchReset">{{ $t('reset') }}</a-button>
</div>
</a-col>
</a-row>
</a-form>
<template>
<!--点击查询后展示的列表-->
<div class="search-node-list" :class="{'hide-search': !showSearch}">
<div class="search-node-item" v-for="node in nodeNameList" @click="handleSearchNode(node)">
<span v-if="hasSearchVal(node.name)">{{
beforeSearchVal(node.name)
}}<span class="highlight-text">{{ searchValInText(node.name) }}</span>{{ afterSearchVal(node.name) }}</span>
<span v-else>{{ node.name }}</span>
</div>
</div>
<!--遮罩-->
<div class="search-overlay" v-show="showSearch" @click="hideSearch"></div>
</template>
</div>
<!--思维导图部分-->
<mind-map ref="mindMap" dom-id="mountNode" :map-data="data" map-data-key-field="name" @select="handleSelectNode" />
<!--新增/编辑-->
<standardization-activity-drawer ref="modalForm" @ok="modalFormOk" />
<!--统计列表-->
<statistical-list-drawer ref="statisticalDrawer" />
</internal-detail-page>
</template>
<script>
import MindMap from '../../../components/MindMap.vue'
import InternalDetailPage from '../../../components/InternalDetailPage'
import { queryTreeList } from '../../../api/businessSupport'
import StandardizationActivityDrawer from './modules/StandardizationActivityDrawer'
import StatisticalListDrawer from './modules/StatisticalListDrawer'
export default {
name: 'StandardizationActivityPage',
components: { InternalDetailPage, MindMap, StandardizationActivityDrawer, StatisticalListDrawer },
data () {
return {
nodeName: '',
nodeNameList: [], // 查询的下拉数据
defaultNodeNameList: [], // 查询的下拉数据
data: {},
currentNodeName: '',
showSearch: false,
loading: false,
selectedNodeId: null, // 选中节点的信息
selectedNode: {}
}
},
created () {
this.loadData()
},
methods: {
/**
* 获取数据
*/
loadData () {
this.loading = true
queryTreeList().then(res => {
if (res.success) {
this.data = Object.assign({}, res.result)
this.treeToArr([this.data], null, this.defaultNodeNameList)
}else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleSearch () {
if (!this.nodeName) {
this.nodeNameList = []
return
}
this.nodeNameList = this.defaultNodeNameList.filter(tt => tt.name.toLowerCase().indexOf(this.nodeName.toLowerCase()) !== -1)
},
searchReset () {
this.nodeName = null
this.nodeNameList = []
this.currentNodeName = null
this.selectedNode = {}
this.selectedNodeId = null
this.$refs.mindMap.searchNode()
this.hideSearch()
},
searchQuery () {
this.showSearch = true
},
hideSearch () {
this.showSearch = false
},
treeToArr (data, pid = null, res) {
data.forEach(item => {
res.push(item)
if (item.children && item.children.length !== 0) {
this.treeToArr(item.children, item.id, res)
}
})
return res
},
/**
* 是否包含搜索的文本
* @param text
* @returns {boolean}
*/
hasSearchVal (text) {
const filterText = text || ''
const searchVal = this.nodeName || ''
return text && filterText.toLowerCase().indexOf(searchVal.toLowerCase()) !== -1
},
/**
* 查询关键字之前的
* @param text
* @returns {string}
*/
beforeSearchVal (text) {
const filterText = text || ''
const searchVal = this.nodeName || ''
const index = filterText.toLowerCase().indexOf(searchVal.toLowerCase())
return filterText.substring(0, index)
},
/**
* 为了保留大小写,需要吧查询的内容也特殊处理一下
* @param text
* @returns {string}
*/
searchValInText (text) {
const filterText = text || ''
const searchVal = this.nodeName || ''
const index = filterText.toLowerCase().indexOf(searchVal.toLowerCase())
return filterText.substring(index, index + searchVal.length)
},
/**
* 输入内容后面的部分
* @param text
* @returns {string}
*/
afterSearchVal (text) {
const filterText = text || ''
const searchVal = this.nodeName || ''
const index = filterText.toLowerCase().indexOf(searchVal.toLowerCase())
return filterText.substring(index + searchVal.length)
},
handleSearchNode (node) {
this.currentNodeName = node.name
this.$refs.mindMap.searchNode(node.id)
},
/**
* 选中/取消选中节点
* @param node
*/
handleSelectNode (node) {
this.selectedNodeId = node.id
this.currentNodeName = node.name
this.selectedNode = Object.assign({}, node || {})
},
modalFormOk () {
this.loadData()
},
handleAdd () {
// 需要先选中一个节点
if (!this.selectedNodeId) {
this.$message.warning(this.$t('businessSupport.standardizationActivity.pleaseSelectNode'))
return
}
// 只能加四级
if (this.selectedNode.level + '' === '3') {
this.$message.warning(this.$t('businessSupport.standardizationActivity.addUpToFourLevels'))
return
}
this.$refs.modalForm.add({
pid: this.selectedNodeId,
supperName: this.selectedNode.name,
level: this.selectedNode.level + 1
})
},
handleEdit () {
// 需要先选中一个节点
if (!this.selectedNodeId) {
this.$message.warning(this.$t('businessSupport.standardizationActivity.pleaseSelectNode'))
return
}
this.$refs.modalForm.edit(this.selectedNodeId)
},
handleDetail () {
// 需要先选中一个节点
if (!this.selectedNodeId) {
this.$message.warning(this.$t('businessSupport.standardizationActivity.pleaseSelectNode'))
return
}
this.$refs.modalForm.view(this.selectedNodeId)
},
handleStatistical () {
this.$refs.statisticalDrawer.open()
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
#mountNode {
width: calc(100% - 48px);
height: calc(100% - 56px - 48px);
margin: 0 24px 24px 24px;
}
.search-box {
width: 564px;
padding: 24px 24px 0;
box-sizing: border-box;
}
.search-box-shadow {
box-shadow: rgba(0, 0, 0, 0.15) 2.4px 0 3.2px;
}
.page-icon {
font-size: 21px;
color: @primary-color;
margin-right: 12px;
}
/deep/ .table-page-search-wrapper .ant-form-inline .ant-form-item > .ant-form-item-label {
min-width: unset;
}
/deep/ .title-current-node {
display: inline-block;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
color: #4E5969;
margin-left: 12px;
}
/deep/ .page-title-sub-title {
max-width: 15rem;
overflow: hidden;
text-overflow: ellipsis;
}
.page-box {
position: relative;
}
.search-node-list {
position: absolute;
width: 564px;
transition: width .2s linear;
top: 136px;
left: 0;
bottom: 0;
background-color: #FFFFFF;
box-shadow: 2.4px 1px 3.2px rgba(0, 0, 0, 0.15);
box-sizing: border-box;
border-top: 1px solid #E5E6EB;
padding: 8px;
overflow: auto;
z-index: 99;
}
.hide-search {
width: 0;
display: none;
}
.search-node-item {
height: 40px;
line-height: 40px;
padding: 0 12px;
cursor: pointer;
font-size: 14px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
color: #1D2129;
}
.search-node-item:hover {
background: rgba(29, 33, 41, 0.06);
}
.highlight-text {
color: @primary-color;
}
.search-overlay {
position: absolute;
top: 56px;
left: 564px;
right: 0;
bottom: 0;
z-index: 98;
}
.custom-operate {
.ant-btn {
margin-left: 12px;
}
.ant-btn:first-child {
margin-left: 0;
}
}
</style>
@@ -0,0 +1,178 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
:confirmLoading="confirmLoading"
:ok-text="$t('preservation')"
:cancel-text="$t('cancel')"
@ok="handleOk"
@cancel="handleCancel">
<a-form :form="form">
<!--联络人-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('system.contact.contactPerson')">
<a-input :placeholder="$t('pleaseEnter') + $t('system.contact.contactPerson')"
:maxLength="50"
v-decorator="['liaison', validatorRules.liaison]" />
</a-form-item>
<!--单位-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.unit')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.unit')"
:maxLength="50"
v-decorator="['unit', validatorRules.unit]" />
</a-form-item>
<!--电话-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.phone')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.phone')"
:maxLength="50"
v-decorator="['phone', validatorRules.phone]" />
</a-form-item>
<!--邮箱-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('user.mailbox')">
<a-input :placeholder="$t('pleaseEnter') + $t('user.mailbox')"
:maxLength="50"
v-decorator="['mailbox', validatorRules.mailbox]" />
</a-form-item>
</a-form>
</j-modal>
</template>
<script>
import { phoneReg, isEmail } from '../../../../utils/validate'
export default {
name: 'ContactModal',
data () {
return {
title: this.$t('businessSupport.standardizationActivity.addContactInformation'),
width: 800,
visible: false,
confirmLoading: false,
model: {},
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
},
validatorRules: {
// 联络人
liaison: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('system.contact.contactPerson')
}],
validateTrigger: 'blur'
},
// 单位
unit: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.unit')
}],
validateTrigger: 'blur'
},
// 电话
phone: {
rules: [{
required: true, validator: this.checkPhone
}],
validateTrigger: 'blur'
},
// 邮箱
mailbox: {
rules: [{
required: true, validator: this.checkEmail
}],
validateTrigger: 'blur'
}
}
}
},
methods: {
add () {
this.visible = true
},
view (record) {
this.visible = true
this.model = Object.assign({}, record)
this.disabled = true
this.$nextTick(() => {
this.form.setFieldsValue(this.model)
})
},
handleOk () {
this.form.validateFields((errors, values) => {
if (!errors) {
const formData = Object.assign(this.model, values)
this.$emit('ok', formData)
this.close()
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.model = {}
this.form.resetFields()
},
/**
* 校验电话
* @param rule
* @param value
* @param callback
*/
checkPhone (rule, value, callback) {
if (!value) {
// 请输入电话
callback(new Error(this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.phone')))
return
}
if (phoneReg(value)) {
callback()
return
}
// 请输入正确格式的电话
callback(new Error(this.$t('businessSupport.standardizationActivity.pleaseEnterCorrectPhone')))
},
/**
* 校验邮箱
* @param rule
* @param value
* @param callback
*/
checkEmail (rule, value, callback) {
if (!value) {
callback(new Error(this.$t('pleaseEnter') + this.$t('user.mailbox')))
return
}
if (isEmail(value)) {
callback()
return
}
callback(this.$t('user.enterMailboxCorrect'))
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,56 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
@cancel="handleCancel">
<contact-table :data-source="dataSource" :need-row-selection="false" :loading="loading" disabled />
<template slot="footer">
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import ContactTable from '../tables/ContactTable'
import { queryContactList } from '../../../../api/businessSupport'
export default {
name: 'ContactTableModal',
components: { ContactTable },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.contactPersonContactInformation'),
width: 800,
visible: false,
loading: false,
dataSource: []
}
},
methods: {
open (id) {
this.visible = true
this.loading = true
queryContactList({ id }).then(res => {
if (res.success) {
this.dataSource = res.result || []
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,247 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
:confirmLoading="confirmLoading"
:ok-text="$t('preservation')"
:cancel-text="$t('cancel')"
@ok="handleOk"
@cancel="handleCancel">
<a-form :form="form">
<!--我司在工作组排名顺序-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.order')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.order')"
:maxLength="50"
v-decorator="['sort', validatorRules.sort]" />
</a-form-item>
<!--我司人员-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.ourPersonnel')">
<user-selection :placeholder="$t('pleaseSelect') + $t('businessSupport.standardizationActivity.ourPersonnel')"
type="radio"
v-decorator="['personnelId', validatorRules.personnelId]"
:name-str="model.personnel"
@listChange="handleUserChange" />
</a-form-item>
<!--工号-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('user.workNo')">
<a-input :placeholder="$t('pleaseEnter') + $t('user.workNo')"
:maxLength="50"
disabled
v-decorator="['jobNum', validatorRules.jobNum]" />
</a-form-item>
<!--单位-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.unit')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.unit')"
:maxLength="50"
v-decorator="['unit', validatorRules.unit]" />
</a-form-item>
<!--部门-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('department')">
<a-input :placeholder="$t('pleaseEnter') + $t('department')"
:maxLength="50"
disabled
v-decorator="['department', validatorRules.department]" />
</a-form-item>
<!--电话-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.phone')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.phone')"
:maxLength="50"
disabled
v-decorator="['phone', validatorRules.phone]" />
</a-form-item>
<!--邮箱-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('user.mailbox')">
<a-input :placeholder="$t('pleaseEnter') + $t('user.mailbox')"
:maxLength="50"
disabled
v-decorator="['mailbox', validatorRules.mailbox]" />
</a-form-item>
<!--身份-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('user.identity')">
<a-input :placeholder="$t('pleaseEnter') + $t('user.identity')"
:maxLength="50"
v-decorator="['identity', validatorRules.identity]" />
</a-form-item>
</a-form>
</j-modal>
</template>
<script>
import UserSelection from '../../../../components/selection/UserSelection'
import { isEmail, phoneReg } from '../../../../utils/validate'
export default {
name: 'GroupModal',
components: { UserSelection },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.addParticipation'),
width: 800,
visible: false,
confirmLoading: false,
model: {},
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 18 }
},
validatorRules: {
// 我司在工作组排名顺序
sort: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.order') }],
validateTrigger: 'blur'
},
// 我司人员
personnelId: {
rules: [{
required: true,
message: this.$t('pleaseSelect') + this.$t('businessSupport.standardizationActivity.ourPersonnel')
}],
validateTrigger: 'change'
},
// 工号
jobNum: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('user.workNo') }],
validateTrigger: 'blur'
},
// 单位
unit: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.unit') }],
validateTrigger: 'blur'
},
// 部门
department: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('department') }],
validateTrigger: 'blur'
},
// 电话
phone: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.phone') }],
validateTrigger: 'blur'
},
// 邮箱
mailbox: { rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('user.mailbox') }], validateTrigger: 'blur' },
// 身份
identity: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('user.identity') }],
validateTrigger: 'blur'
}
},
disabled: false
}
},
methods: {
add () {
this.visible = true
},
view (record) {
this.visible = true
this.model = Object.assign({}, record)
this.disabled = true
this.$nextTick(() => {
this.form.setFieldsValue(this.model)
})
},
handleOk () {
this.form.validateFields((errors, values) => {
if (!errors) {
console.log(JSON.parse(JSON.stringify(this.model)))
console.log(JSON.parse(JSON.stringify(values)))
const formData = Object.assign(this.model, values)
console.log(formData)
this.$emit('ok', formData)
this.close()
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.model = {}
this.form.resetFields()
},
handleUserChange (list) {
const data = list[0] || {}
const obj = {
jobNum: data.username, // 工号
mailbox: data.email, // 邮箱
department: data.orgCodeTxt, // 部门
phone: data.phone, // 电话
personnel: data.realname
}
this.model = Object.assign(this.model, obj)
this.form.setFieldsValue(obj)
},
/**
* 校验电话
* @param rule
* @param value
* @param callback
*/
checkPhone (rule, value, callback) {
if (!value) {
// 请输入电话
callback(new Error(this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.phone')))
return
}
if (phoneReg(value)) {
callback()
return
}
// 请输入正确格式的电话
callback(new Error(this.$t('businessSupport.standardizationActivity.pleaseEnterCorrectPhone')))
},
/**
* 校验邮箱
* @param rule
* @param value
* @param callback
*/
checkEmail (rule, value, callback) {
if (!value) {
callback(new Error(this.$t('pleaseEnter') + this.$t('user.mailbox')))
return
}
if (isEmail(value)) {
callback()
return
}
callback(this.$t('user.enterMailboxCorrect'))
},
handleDepartChange (departInfo) {
const names = departInfo.map(tt => {
return tt.text
})
this.model.department_dictText = names.join(',')
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,56 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
@cancel="handleCancel">
<group-table :data-source="dataSource" :need-row-selection="false" :loading="loading" disabled />
<template slot="footer">
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import { queryGroupList } from '../../../../api/businessSupport'
import GroupTable from '../tables/GroupTable'
export default {
name: 'GroupTableModal',
components: { GroupTable },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.groupParticipation'),
width: 800,
visible: false,
loading: false,
dataSource: []
}
},
methods: {
open (id) {
this.visible = true
this.loading = true
queryGroupList({ id }).then(res => {
if (res.success) {
this.dataSource = res.result || []
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,150 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
:confirmLoading="confirmLoading"
@cancel="handleCancel">
<a-form :form="form">
<!--参会日期-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('workCenter.postMeetingManageProcess.attendanceDate')">
<j-date :placeholder="$t('pleaseSelect') + $t('workCenter.postMeetingManageProcess.attendanceDate')"
show-type="day"
date-format="YYYY-MM-DD"
:disabled="disabled"
v-decorator="['participationTime', validatorRules.participationTime]" />
</a-form-item>
<!--会议名称-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('workCenter.postMeetingManageProcess.meetingName')">
<a-input :placeholder="$t('pleaseEnter') + $t('workCenter.postMeetingManageProcess.meetingName')"
:maxLength="50"
:disabled="disabled"
v-decorator="['name', validatorRules.name]" />
</a-form-item>
<!--参会人员-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('workCenter.enStandardRevision.conferee')">
<user-selection :placeholder="$t('pleaseSelect') + $t('workCenter.enStandardRevision.conferee')"
:disabled="disabled"
type="checkbox"
:name-str="model.personnel"
v-decorator="['personnelId', validatorRules.personnelId]" @nameChange="handleUserNameChange" />
</a-form-item>
<!--会议资料-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.meetingMaterial')"
v-if="disabled">
<file-echo :file-ids="model.meetingData" />
</a-form-item>
</a-form>
<template slot="footer">
<a-button @click="handleCancel" v-if="disabled">{{ $t('close') }}</a-button>
<template v-else>
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button type="primary" @click="handleOk">{{ $t('preservation') }}</a-button>
</template>
</template>
</j-modal>
</template>
<script>
import UserSelection from '../../../../components/selection/UserSelection'
import FileEcho from '../../../../components/FileEcho'
export default {
name: 'MeetingModal',
components: { FileEcho, UserSelection },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.attendanceRecord'),
width: 800,
visible: false,
confirmLoading: false,
model: {},
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
},
validatorRules: {
// 参会日期
participationTime: {
rules: [{
required: true,
message: this.$t('pleaseSelect') + this.$t('workCenter.postMeetingManageProcess.attendanceDate')
}],
validateTrigger: 'change'
},
// 会议名称
name: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('workCenter.postMeetingManageProcess.meetingName')
}],
validateTrigger: 'blur'
},
// 参会人员
personnelId: {
rules: [{
required: true,
message: this.$t('pleaseSelect') + this.$t('workCenter.enStandardRevision.conferee')
}],
validateTrigger: 'change'
}
},
disabled: false
}
},
methods: {
add () {
this.disabled = false
this.visible = true
this.model.source = '1'
},
view (record) {
this.visible = true
this.model = Object.assign({}, record)
this.disabled = true
this.$nextTick(() => {
this.form.setFieldsValue(this.model)
})
},
handleOk () {
this.form.validateFields((errors, values) => {
if (!errors) {
const formData = Object.assign(this.model, values)
this.$emit('ok', formData)
this.close()
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.model = {}
this.form.resetFields()
},
handleUserNameChange (name) {
this.model.personnel = name
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,56 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
@cancel="handleCancel">
<meeting-table :data-source="dataSource" :need-row-selection="false" :loading="loading" disabled />
<template slot="footer">
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import MeetingTable from '../tables/MeetingTable'
import { queryMeetingList } from '../../../../api/businessSupport'
export default {
name: 'MeetingTableModal',
components: { MeetingTable },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.attendanceRecord'),
width: 800,
visible: false,
loading: false,
dataSource: []
}
},
methods: {
open (id) {
this.visible = true
this.loading = true
queryMeetingList({ id }).then(res => {
if (res.success) {
this.dataSource = res.result || []
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,181 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
:confirmLoading="confirmLoading"
:ok-text="$t('preservation')"
:cancel-text="$t('cancel')"
@ok="handleOk"
@cancel="handleCancel">
<a-form :form="form">
<!--费用-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.cost')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.cost')"
:maxLength="50"
v-decorator="['fee', validatorRules.fee]" />
</a-form-item>
<!--年份-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.year')">
<j-date :placeholder="$t('pleaseSelect') + $t('businessSupport.standardizationActivity.year')"
show-type="year"
date-format="YYYY"
v-decorator="['year', validatorRules.year]" />
</a-form-item>
<!--状态-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('status')">
<j-dict-select-tag
v-decorator="['state', validatorRules.state]"
:placeholder="$t('pleaseSelect') + $t('status')"
:type="'select'"
:triggerChange="false"
dictCode="pay_state"
@change="handleStateChange" />
</a-form-item>
<!--计划内-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.plan')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.plan')"
:maxLength="50"
v-decorator="['plan', validatorRules.plan]" />
</a-form-item>
<!--缴费方式-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.paymentMethod')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.paymentMethod')"
:maxLength="50"
v-decorator="['pay', validatorRules.pay]" />
</a-form-item>
<!--费用列支-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.expenseReimbursement')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.expenseReimbursement')"
:maxLength="50"
v-decorator="['feeListing', validatorRules.feeListing]" />
</a-form-item>
</a-form>
</j-modal>
</template>
<script>
import { PayState } from '../../../../enums/commonEnums'
export default {
name: 'PaymentModal',
data () {
return {
title: this.$t('businessSupport.standardizationActivity.paymentInfo'),
width: 800,
visible: false,
confirmLoading: false,
model: {},
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
},
validatorRules: {
// 费用
fee: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.cost')
}],
validateTrigger: 'blur'
},
// 年份
year: {
rules: [{
required: true,
message: this.$t('pleaseSelect') + this.$t('businessSupport.standardizationActivity.year')
}],
validateTrigger: 'change'
},
// 状态
state: {
rules: [{
required: true,
message: this.$t('pleaseSelect') + this.$t('status')
}],
validateTrigger: 'change'
},
// 计划内
plan: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.plan')
}],
validateTrigger: 'blur'
},
// 缴费方式
pay: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.paymentMethod')
}],
validateTrigger: 'blur'
},
// 费用列支
feeListing: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.expenseReimbursement')
}],
validateTrigger: 'blur'
}
}
}
},
methods: {
add () {
this.visible = true
},
view (record) {
this.visible = true
this.model = Object.assign({}, record)
this.disabled = true
this.$nextTick(() => {
this.form.setFieldsValue(this.model)
})
},
handleOk () {
this.form.validateFields((errors, values) => {
if (!errors) {
const formData = Object.assign(this.model, values)
this.$emit('ok', formData)
this.close()
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.model = {}
this.form.resetFields()
},
handleStateChange (value) {
this.model.state_dictText = PayState.nameOfIndex(value)
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,56 @@
<template>
<j-modal
:title="title"
:width="width"
:visible="visible"
:maskClosable="false"
@cancel="handleCancel">
<payment-table :data-source="dataSource" :need-row-selection="false" :loading="loading" disabled />
<template slot="footer">
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import PaymentTable from '../tables/PaymentTable'
import { queryPaymentList } from '../../../../api/businessSupport'
export default {
name: 'PaymentTableModal',
components: { PaymentTable },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.paymentInfo'),
width: 800,
visible: false,
loading: false,
dataSource: []
}
},
methods: {
open (id) {
this.visible = true
this.loading = true
queryPaymentList({ id }).then(res => {
if (res.success) {
this.dataSource = res.result || []
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,351 @@
<template>
<a-drawer
:title="title"
:width="width"
placement="right"
:closable="true"
@close="handleCancel"
:visible="visible"
:maskClosable="false"
destroyOnClose
class="custom-drawer-style">
<div class="custom-drawer-style-scroll">
<a-spin :spinning="confirmLoading">
<!--基础信息-->
<div class="header-text">{{ $t('basicInformation') }}</div>
<a-form :form="form">
<!--上级节点-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.parentNode')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.parentNode')"
:maxLength="50"
disabled
v-decorator="['supperName', validatorRules.supperName]" />
</a-form-item>
<!--节点类型-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.nodeTypes')">
<j-dict-select-tag :placeholder="$t('pleaseSelect') + $t('businessSupport.standardizationActivity.nodeTypes')"
v-decorator="['standardizationType', validatorRules.standardizationType]"
disabled
dict-code="standardization_type" />
</a-form-item>
<!--编号-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('number')">
<a-input :placeholder="$t('pleaseEnter') + $t('number')"
:maxLength="50"
:disabled="disabled"
v-decorator="['num', validatorRules.num]" />
</a-form-item>
<!--名称-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('name')">
<a-input :placeholder="$t('pleaseEnter') + $t('name')"
:maxLength="50"
:disabled="disabled"
v-decorator="['name', validatorRules.name]" />
</a-form-item>
<!--标准化工作领域-->
<a-form-item :labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('businessSupport.standardizationActivity.standardizationWorkArea')">
<a-input :placeholder="$t('pleaseEnter') + $t('businessSupport.standardizationActivity.standardizationWorkArea')"
:maxLength="100"
:disabled="disabled"
v-decorator="['workingArea', validatorRules.workingArea]" />
</a-form-item>
<!--备注-->
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('remarks')">
<a-textarea :placeholder="$t('pleaseEnter') + $t('remarks')"
:maxLength="500"
:rows="4"
:disabled="disabled"
v-decorator="['notes', validatorRules.notes]" />
</a-form-item>
</a-form>
<!--联络人联系方式-->
<div class="header-text">{{ $t('businessSupport.standardizationActivity.contactPersonContactInformation') }}</div>
<contact-table :data-source="contactDataSource"
:disabled="disabled"
:need-row-selection="!disabled"
@ok="contactModalOk" />
<!--集团参与情况-->
<div class="header-text">{{ $t('businessSupport.standardizationActivity.groupParticipation') }}</div>
<group-table ref="groupTable" :data-source="groupDataSource" :disabled="disabled" :need-row-selection="!disabled" @ok="groupModalOk" />
<!--支付信息-->
<div class="header-text">{{ $t('businessSupport.standardizationActivity.paymentInfo') }}</div>
<payment-table ref="paymentTable"
:data-source="paymentDataSource"
:disabled="disabled"
:need-row-selection="!disabled"
@ok="paymentModalOk" />
<!--参会记录-->
<div class="header-text">{{ $t('businessSupport.standardizationActivity.attendanceRecord') }}</div>
<meeting-table ref="meetingTable"
:data-source="meetingDataSource"
:disabled="disabled"
:need-row-selection="!disabled"
@ok="meetingModalOk" />
</a-spin>
</div>
<div class="custom-drawer-style-bottom-btn">
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button type="primary" @click="handleOk" v-if="!disabled">{{ $t('determine') }}</a-button>
</div>
</a-drawer>
</template>
<script>
import { StandardizedActivityType } from '../../../../enums/commonEnums'
import JTable from '../../../../components/jero/JTable'
import ContactModal from './ContactModal'
import GroupModal from './GroupModal'
import PaymentModal from './PaymentModal'
import MeetingModal from './MeetingModal'
import { addNode, editNode, queryTableDataById } from '../../../../api/businessSupport'
import ContactTable from '../tables/ContactTable'
import GroupTable from '../tables/GroupTable'
import PaymentTable from '../tables/PaymentTable'
import MeetingTable from '../tables/MeetingTable'
export default {
name: 'StandardizationActivityDrawer',
components: {
JTable,
ContactModal,
GroupModal,
PaymentModal,
MeetingModal,
ContactTable,
GroupTable,
PaymentTable,
MeetingTable
},
data () {
return {
title: this.$t('newlyAdded'),
width: 800,
visible: false,
confirmLoading: false,
model: {},
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
},
validatorRules: {
// 上级节点
supperName: {
rules: [{
required: false,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.parentNode')
}],
validateTrigger: 'blur'
},
// 节点类型
standardizationType: {
rules: [{
required: false,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.nodeTypes')
}],
validateTrigger: 'blur'
},
// 编号
num: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('number')
}],
validateTrigger: 'blur'
},
// 名称
name: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('name')
}],
validateTrigger: 'blur'
},
// 标准化工作领域
workingArea: {
rules: [{
required: true,
message: this.$t('pleaseEnter') + this.$t('businessSupport.standardizationActivity.standardizationWorkArea')
}],
validateTrigger: 'blur'
},
// 备注
notes: {
rules: [{
required: false,
message: this.$t('pleaseEnter') + this.$t('remarks')
}]
}
},
// 联络人联系方式表格
contactDataSource: [],
// 集团参与情况
groupDataSource: [],
// 支付信息
paymentDataSource: [],
// 参会记录
meetingDataSource: [],
uploadTable: {}, // 上传文件
disabled: false
}
},
methods: {
add (record) {
this.title = this.$t('newlyAdded')
this.visible = true
this.disabled = false
switch (record.level) {
case 1:
record.standardizationType = StandardizedActivityType.secondLevelBidCommittee.value
break
case 2:
record.standardizationType = StandardizedActivityType.workingTeam.value
break
case 3:
record.standardizationType = StandardizedActivityType.fourLevelBiddingCommittee.value
break
default:
break
}
this.model = Object.assign({}, record)
this.$nextTick(() => {
this.form.setFieldsValue(this.model)
})
},
edit (id) {
this.title = this.$t('edit')
this.visible = true
this.disabled = false
this.getDetailData(id)
},
view (id) {
this.title = this.$t('view')
this.visible = true
this.disabled = true
this.getDetailData(id)
},
getDetailData (id) {
this.confirmLoading = true
// 获取详情数据
queryTableDataById({ id }).then(res => {
if (res.success) {
const result = res.result || {}
// 基础信息
this.model = Object.assign({}, result.lawsStandardization || {})
this.$nextTick(() => {
this.form.setFieldsValue(this.model)
})
// 联络人联系方式
this.contactDataSource = result.standardizationLiaisonList
// 集团参与情况
this.groupDataSource = result.standardizationGroupList
// 支付信息
this.paymentDataSource = result.standardizationPayments
// 参会记录
this.meetingDataSource = result.standardizationMeetings
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
},
handleOk () {
const groupValidateFlag = this.$refs.groupTable.validateTableData()
const paymentValidateFlag = this.$refs.paymentTable.validateTableData()
const meetingValidateFlag = this.$refs.meetingTable.validateTableData()
if (!groupValidateFlag || !paymentValidateFlag || !meetingValidateFlag) {
return
}
this.form.validateFields((errors, values) => {
if (!errors) {
const formData = {
lawsStandardization: Object.assign(this.model, values), // 基础信息
standardizationLiaisonList: this.contactDataSource, //联络人联系方式
standardizationGroupList: this.groupDataSource, // 集团参与情况
standardizationPayments: this.paymentDataSource, // 支付记录
standardizationMeetings: this.meetingDataSource // 参会记录
}
this.confirmLoading = true
let submitFunc
if (this.model.id) {
submitFunc = editNode
} else {
submitFunc = addNode
}
submitFunc(formData).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.form.resetFields()
this.model = {}
this.contactDataSource = []
this.groupDataSource = []
this.paymentDataSource = []
this.meetingDataSource = []
},
contactModalOk (data) {
this.contactDataSource = JSON.parse(JSON.stringify(data))
},
groupModalOk (data) {
this.groupDataSource = JSON.parse(JSON.stringify(data))
},
paymentModalOk (data) {
this.paymentDataSource = JSON.parse(JSON.stringify(data))
},
meetingModalOk (data) {
this.meetingDataSource = JSON.parse(JSON.stringify(data))
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.header-text {
margin-top: 30px;
margin-bottom: 15px;
}
.header-text:first-child {
margin-top: 0;
}
</style>
@@ -0,0 +1,209 @@
<template>
<a-drawer
:title="title"
:width="width"
placement="right"
:closable="true"
@close="handleCancel"
:visible="visible"
:maskClosable="false"
destroyOnClose
class="custom-drawer-style">
<div class="custom-drawer-style-scroll">
<j-table :scroll="{x: '100%', y: 'calc(100vh - 56px - 61px - 48px - 54px)'}"
:pagination="false"
rowKey="id"
:loading="loading"
:columns="columns"
:dataSource="dataSource"
:operation-list="operationList"
@operationClick="operationClick">
</j-table>
</div>
<div class="custom-drawer-style-bottom-btn">
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
</div>
<!--联络人联系方式-->
<contact-table-modal ref="contactTableModal" />
<!--集团参与情况-->
<group-table-modal ref="groupTableModal" />
<!--支付信息-->
<payment-table-modal ref="paymentTableModal" />
<!--参会记录-->
<meeting-table-modal ref="meetingTableModal" />
</a-drawer>
</template>
<script>
import JTable from '../../../../components/jero/JTable'
import { queryStatisticalList } from '../../../../api/businessSupport'
import ContactTableModal from './ContactTableModal'
import GroupTableModal from './GroupTableModal'
import PaymentTableModal from './PaymentTableModal'
import MeetingTableModal from './MeetingTableModal'
export default {
name: 'StatisticalListDrawer',
components: { JTable, ContactTableModal, GroupTableModal, PaymentTableModal, MeetingTableModal },
data () {
return {
title: this.$t('businessSupport.standardizationActivity.conferenceStatisticsList'),
width: 1200,
visible: false,
loading: false,
columns: [
// 序号
{
title: this.$t('serialNumber'),
key: 'rowIndex',
width: 60,
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
// 上级节点
{
title: this.$t('businessSupport.standardizationActivity.parentNode'),
width: 150,
dataIndex: 'supperName'
},
// 节点类型
{
title: this.$t('businessSupport.standardizationActivity.nodeTypes'),
width: 100,
dataIndex: 'standardizationType_dictText'
},
// 编号
{
title: this.$t('number'),
width: 150,
dataIndex: 'num'
},
// 名称
{
title: this.$t('name'),
width: 150,
dataIndex: 'name'
},
// 标准化工作领域
{
title: this.$t('businessSupport.standardizationActivity.standardizationWorkArea'),
width: 130,
dataIndex: 'workingArea'
},
// 备注
{
title: this.$t('remarks'),
width: 150,
dataIndex: 'notes'
},
// 操作
{
title: this.$t('operation'),
align: 'center',
fixed: 'right',
width: 200,
scopedSlots: { customRender: 'action' }
}
],
dataSource: [],
operationList: [
// 联络人联系方式
{
text: this.$t('businessSupport.standardizationActivity.contactPersonContactInformation'),
clickEvent: 'handleViewContact'
// has: 'enterpriseStandardLibrary:plan:delete'
},
// 集团参与情况
{
text: this.$t('businessSupport.standardizationActivity.groupParticipation'),
clickEvent: 'handleViewGroup'
// has: 'enterpriseStandardLibrary:plan:delete'
},
// 支付信息
{
text: this.$t('businessSupport.standardizationActivity.paymentInfo'),
clickEvent: 'handleViewPayment'
// has: 'enterpriseStandardLibrary:plan:delete'
},
// 参会记录
{
text: this.$t('businessSupport.standardizationActivity.attendanceRecord'),
clickEvent: 'handleViewMeeting'
// has: 'enterpriseStandardLibrary:plan:delete'
}
]
}
},
methods: {
open () {
this.visible = true
this.loading = true
queryStatisticalList().then(res => {
if (res.success) {
this.dataSource = res.result || []
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
},
/**
* 循环操作按钮的操作
* @param operation
* @param record
*/
operationClick (operation, record) {
if (operation.clickEvent === 'handleDelete') {
this.handleDelete(record.id)
} else {
this[operation.clickEvent](record)
}
},
/**
* 查看联络人联系方式
* @param id
*/
handleViewContact ({ id }) {
this.$refs.contactTableModal.open(id)
},
/**
* 查看集团参与情况
* @param id
*/
handleViewGroup ({ id }) {
this.$refs.groupTableModal.open(id)
},
/**
* 查看支付信息
* @param id
*/
handleViewPayment ({ id }) {
this.$refs.paymentTableModal.open(id)
},
/**
* 查看参会记录
* @param id
*/
handleViewMeeting ({ id }) {
this.$refs.meetingTableModal.open(id)
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
</style>
@@ -0,0 +1,131 @@
<template>
<div>
<div class="table-operator" v-if="!disabled">
<a-button type="primary" ghost icon="plus" @click="handleAdd">
{{ $t('businessSupport.standardizationActivity.addContactInformation') }}
</a-button>
<!--删除-->
<a-button type="primary" icon="delete" ghost @click="handleDelete">{{ $t('delete') }}</a-button>
</div>
<j-table v-bind="$attrs"
:scroll="{x: '100%'}"
:pagination="false"
:rowKey="(record,index)=>{return index}"
:columns="columns"
:dataSource="realDataSource"
:row-selection="needRowSelection ? { selectedRowKeys: selectedRowKeys, onChange: onSelectChange } : null">
</j-table>
<!--新增弹框-->
<contact-modal ref="formModal" @ok="formModalOk" />
</div>
</template>
<script>
import JTable from '../../../../components/jero/JTable'
import ContactModal from '../modules/ContactModal'
export default {
name: 'ContactTable',
components: { ContactModal, JTable },
props: {
// 数据
dataSource: {
type: Array,
required: false,
default: () => {
return []
}
},
disabled: {
type: Boolean,
required: false,
default: false
},
// 是否需要行选中
needRowSelection: {
type: Boolean,
required: false,
default: true
}
},
data () {
return {
columns: [
// 联络人
{
title: this.$t('system.contact.contactPerson'),
width: 150,
dataIndex: 'liaison'
},
// 单位
{
title: this.$t('businessSupport.standardizationActivity.unit'),
width: 150,
dataIndex: 'unit'
},
// 电话
{
title: this.$t('businessSupport.standardizationActivity.phone'),
width: 150,
dataIndex: 'phone'
},
// 邮箱
{
title: this.$t('user.mailbox'),
width: 150,
dataIndex: 'mailbox'
}
],
selectedRowKeys: [],
realDataSource: []
}
},
watch: {
dataSource: {
handler (val) {
this.realDataSource = JSON.parse(JSON.stringify(val))
},
immediate: true,
deep: true
}
},
methods: {
// 添加
handleAdd () {
this.$refs.formModal.add()
},
// 删除
handleDelete () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectAtLeastOne'))
return
}
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
const data = JSON.parse(JSON.stringify(this.realDataSource))
this.selectedRowKeys.forEach(index => {
data[index] = null
})
this.selectedRowKeys = []
this.realDataSource = data.filter(tt => !!tt)
this.$emit('ok', this.realDataSource)
}
})
},
onSelectChange (selectedRowKeys) {
this.selectedRowKeys = selectedRowKeys
},
formModalOk (lineData) {
this.realDataSource.push(lineData)
this.$emit('ok', this.realDataSource)
}
}
}
</script>
<style scoped lang="less">
</style>
@@ -0,0 +1,207 @@
<template>
<div>
<div class="table-operator" v-if="!disabled">
<a-button type="primary" ghost icon="plus" @click="handleAdd">
{{ $t('businessSupport.standardizationActivity.addParticipation') }}
</a-button>
<!--删除-->
<a-button type="primary" icon="delete" ghost @click="handleDelete">{{ $t('delete') }}</a-button>
</div>
<j-table v-bind="$attrs"
:scroll="{x: '100%'}"
:pagination="false"
:rowKey="(record,index)=>{return index}"
:columns="columns"
:dataSource="realDataSource"
:row-selection="needRowSelection ? { selectedRowKeys: selectedRowKeys, onChange: onSelectChange } : null">
<template v-slot:file="{text, record, index}">
<a-button type="primary"
class="button-text"
@click="clickButtonToUpload(text, index, 'feeData')">
{{
(text === 'null' || text === '' || text === null || text === undefined) ? $t('uploadFile.clickUpload') : $t('uploadFile.viewUploadedFiles')
}}
</a-button>
</template>
</j-table>
<!--新增弹框-->
<group-modal ref="formModal" @ok="formModalOk" />
<!--文件上传-->
<upload-file ref="uploadFile" @change="uploadFileChange" :return-url="false" :disabled="disabled" />
</div>
</template>
<script>
import JTable from '../../../../components/jero/JTable'
import GroupModal from '../modules/GroupModal'
import ContactModal from '../modules/ContactModal'
import UploadFile from '../../../../components/UploadFile'
export default {
name: 'GroupTable',
components: { UploadFile, ContactModal, JTable, GroupModal },
props: {
// 数据
dataSource: {
type: Array,
required: false,
default: () => {
return []
}
},
disabled: {
type: Boolean,
required: false,
default: false
},
// 是否需要行选中
needRowSelection: {
type: Boolean,
required: false,
default: true
}
},
data () {
return {
columns: [
// 我司在工作组排名顺序
{
title: this.$t('businessSupport.standardizationActivity.order'),
width: 200,
dataIndex: 'sort'
},
// 我司人员
{
title: this.$t('businessSupport.standardizationActivity.ourPersonnel'),
width: 150,
dataIndex: 'personnel'
},
// 工号
{
title: this.$t('user.workNo'),
width: 150,
dataIndex: 'jobNum'
},
// 单位
{
title: this.$t('businessSupport.standardizationActivity.unit'),
width: 150,
dataIndex: 'unit'
},
// 部门
{
title: this.$t('department'),
width: 150,
dataIndex: 'department'
},
// 电话
{
title: this.$t('businessSupport.standardizationActivity.phone'),
width: 150,
dataIndex: 'phone'
},
// 邮箱
{
title: this.$t('user.mailbox'),
width: 150,
dataIndex: 'mailbox'
},
// 身份
{
title: this.$t('user.identity'),
width: 150,
dataIndex: 'identity'
},
// 资料
{
title: this.$t('businessSupport.standardizationActivity.data'),
width: 150,
dataIndex: 'feeData',
scopedSlots: { customRender: 'file' }
}
],
selectedRowKeys: [],
realDataSource: [],
uploadTable: {}
}
},
watch: {
dataSource: {
handler (val) {
this.realDataSource = JSON.parse(JSON.stringify(val))
},
immediate: true,
deep: true
}
},
methods: {
// 添加
handleAdd () {
this.$refs.formModal.add()
},
// 删除
handleDelete () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectAtLeastOne'))
return
}
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
const data = JSON.parse(JSON.stringify(this.realDataSource))
this.selectedRowKeys.forEach(index => {
data[index] = null
})
this.selectedRowKeys = []
this.realDataSource = data.filter(tt => !!tt)
this.$emit('ok', this.realDataSource)
}
})
},
onSelectChange (selectedRowKeys) {
this.selectedRowKeys = selectedRowKeys
},
formModalOk (lineData) {
this.realDataSource.push(lineData)
this.$emit('ok', this.realDataSource)
},
// 点击点击上传按钮
clickButtonToUpload (fileIds, index, dbFieldName) {
this.$refs.uploadFile.open(fileIds)
this.uploadTable = { index, dbFieldName }
},
// 文件上传改变后的回调
uploadFileChange (data) {
console.log(data)
const attIdList = []
if (data && data.length > 0) {
data.map(item => {
attIdList.push(item.id)
})
}
/** 赋值给当前对应的表单文件 */
this.$set(this.realDataSource[this.uploadTable.index], this.uploadTable.dbFieldName, attIdList.join(','))
this.$emit('ok', this.realDataSource)
},
/**
* 校验文件是否上传
* @returns {boolean}
*/
validateTableData () {
return true
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.button-text {
width: 100%;
}
</style>
@@ -0,0 +1,211 @@
<template>
<div>
<div class="table-operator" v-if="!disabled">
<a-button type="primary" ghost icon="plus" @click="handleAdd">
{{ $t('businessSupport.standardizationActivity.addAttendanceRecord') }}
</a-button>
<!--删除-->
<a-button type="primary" icon="delete" ghost @click="handleDelete">{{ $t('delete') }}</a-button>
</div>
<j-table v-bind="$attrs"
:scroll="{x: '100%'}"
:pagination="ipagination"
rowKey="tableRowKey"
:columns="columns"
:dataSource="realDataSource"
:row-selection="needRowSelection ? { selectedRowKeys: selectedRowKeys, onChange: onSelectChange } : null"
@change="handleTableChange">
<template v-slot:file="{text, record, index}">
<!--会后资料如果数据从流程来就是查看如果是新增的就正常显示上传-->
<a-button type="primary"
class="button-text"
@click="clickButtonToUpload(text, index, 'meetingData')" v-if="record.source !== '2'">
{{
(text === 'null' || text === '' || text === null || text === undefined) ? $t('uploadFile.clickUpload') : $t('uploadFile.viewUploadedFiles')
}}
<a></a>
</a-button>
<div class="action-span-cell" v-else>
<a @click="handleView(record)">{{ $t('view') }}</a>
</div>
</template>
</j-table>
<!--新增弹框-->
<meeting-modal ref="formModal" @ok="formModalOk" />
<!--文件上传-->
<upload-file ref="uploadFile" @change="uploadFileChange" :return-url="false" :disabled="disabled" />
</div>
</template>
<script>
import UploadFile from '../../../../components/UploadFile'
import JTable from '../../../../components/jero/JTable'
import MeetingModal from '../modules/MeetingModal'
export default {
name: 'MeetingTable',
components: { MeetingModal, JTable, UploadFile },
props: {
// 数据
dataSource: {
type: Array,
required: false,
default: () => {
return []
}
},
disabled: {
type: Boolean,
required: false,
default: false
},
// 是否需要行选中
needRowSelection: {
type: Boolean,
required: false,
default: true
}
},
data () {
return {
columns: [
// 参会日期
{
title: this.$t('workCenter.postMeetingManageProcess.attendanceDate'),
width: 150,
dataIndex: 'participationTime'
},
// 会议名称
{
title: this.$t('workCenter.postMeetingManageProcess.meetingName'),
width: 150,
dataIndex: 'name'
},
// 参会人员
{
title: this.$t('workCenter.enStandardRevision.conferee'),
width: 150,
dataIndex: 'personnel'
},
// 会议资料
{
title: this.$t('businessSupport.standardizationActivity.meetingMaterial'),
width: 150,
dataIndex: 'meetingData',
scopedSlots: { customRender: 'file' }
}
],
selectedRowKeys: [],
realDataSource: [],
uploadTable: {},
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
}
}
},
watch: {
dataSource: {
handler (val) {
this.realDataSource = JSON.parse(JSON.stringify(val)).map((item, index) => {
item.tableRowKey = index
return item
})
},
immediate: true,
deep: true
}
},
methods: {
// 添加
handleAdd () {
this.$refs.formModal.add()
},
// 删除
handleDelete () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectAtLeastOne'))
return
}
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
const data = JSON.parse(JSON.stringify(this.realDataSource))
this.selectedRowKeys.forEach(tableRowKey => {
const index = data.findIndex(tt => tt && tt.tableRowKey === tableRowKey)
data[index] = null
})
this.selectedRowKeys = []
this.realDataSource = data.filter(tt => !!tt)
this.$emit('ok', this.realDataSource)
}
})
},
onSelectChange (selectedRowKeys) {
this.selectedRowKeys = selectedRowKeys
},
formModalOk (lineData) {
lineData.tableRowKey = this.realDataSource && this.realDataSource.length > 0 ? this.realDataSource[this.realDataSource.length - 1].tableRowKey + 1 : 0
this.realDataSource.push(lineData)
this.$emit('ok', this.realDataSource)
},
// 点击点击上传按钮
clickButtonToUpload (fileIds, index, dbFieldName) {
this.$refs.uploadFile.open(fileIds)
this.uploadTable = { index, dbFieldName }
},
// 文件上传改变后的回调
uploadFileChange (data) {
console.log(data)
const attIdList = []
if (data && data.length > 0) {
data.map(item => {
attIdList.push(item.id)
})
}
/** 赋值给当前对应的表单文件 */
this.$set(this.realDataSource[this.uploadTable.index], this.uploadTable.dbFieldName, attIdList.join(','))
this.$emit('ok', this.realDataSource)
},
handleView (record) {
this.$refs.formModal.view(record)
},
handleTableChange (pagination) {
this.ipagination = pagination
},
/**
* 校验文件是否上传
* @returns {boolean}
*/
validateTableData () {
if (this.realDataSource.some(tt => !tt.meetingData)) {
this.$message.warning(this.$t('businessSupport.standardizationActivity.pleaseUploadMeeting'))
return false
}
return true
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.button-text {
width: 100%;
}
</style>
@@ -0,0 +1,199 @@
<template>
<div>
<div class="table-operator" v-if="!disabled">
<a-button type="primary" ghost icon="plus" @click="handleAdd">
{{ $t('businessSupport.standardizationActivity.addPaymentInfo') }}
</a-button>
<!--删除-->
<a-button type="primary" icon="delete" ghost @click="handleDelete">{{ $t('delete') }}</a-button>
</div>
<j-table v-bind="$attrs"
:scroll="{x: '100%'}"
:pagination="false"
:rowKey="(record,index)=>{return index}"
:columns="columns"
:dataSource="realDataSource"
:row-selection="needRowSelection ? { selectedRowKeys: selectedRowKeys, onChange: onSelectChange } : null">
<template v-slot:file="{text, record, index}">
<a-button type="primary"
class="button-text"
@click="clickButtonToUpload(text, index, 'feeData')">
{{
(text === 'null' || text === '' || text === null || text === undefined) ? $t('uploadFile.clickUpload') : $t('uploadFile.viewUploadedFiles')
}}
</a-button>
</template>
</j-table>
<!--新增弹框-->
<payment-modal ref="formModal" @ok="formModalOk" />
<!--文件上传-->
<upload-file ref="uploadFile" @change="uploadFileChange" :return-url="false" :disabled="disabled" />
</div>
</template>
<script>
import UploadFile from '../../../../components/UploadFile'
import JTable from '../../../../components/jero/JTable'
import PaymentModal from '../modules/PaymentModal'
export default {
name: 'PaymentTable',
components: { PaymentModal, JTable, UploadFile },
props: {
// 数据
dataSource: {
type: Array,
required: false,
default: () => {
return []
}
},
disabled: {
type: Boolean,
required: false,
default: false
},
// 是否需要行选中
needRowSelection: {
type: Boolean,
required: false,
default: true
}
},
data () {
return {
columns: [
// 费用
{
title: this.$t('businessSupport.standardizationActivity.cost'),
width: 150,
dataIndex: 'fee'
},
// 年份
{
title: this.$t('businessSupport.standardizationActivity.year'),
width: 150,
dataIndex: 'year'
},
// 状态
{
title: this.$t('status'),
width: 150,
dataIndex: 'state_dictText'
},
// 计划内
{
title: this.$t('businessSupport.standardizationActivity.plan'),
width: 150,
dataIndex: 'plan'
},
// 缴费方式
{
title: this.$t('businessSupport.standardizationActivity.paymentMethod'),
width: 150,
dataIndex: 'pay'
},
// 费用列支
{
title: this.$t('businessSupport.standardizationActivity.expenseReimbursement'),
width: 150,
dataIndex: 'feeListing'
},
// 资料
{
title: this.$t('businessSupport.standardizationActivity.data'),
width: 150,
dataIndex: 'feeData',
scopedSlots: { customRender: 'file' }
}
],
selectedRowKeys: [],
realDataSource: [],
uploadTable: {}
}
},
watch: {
dataSource: {
handler (val) {
this.realDataSource = JSON.parse(JSON.stringify(val))
},
immediate: true,
deep: true
}
},
methods: {
// 添加
handleAdd () {
this.$refs.formModal.add()
},
// 删除
handleDelete () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectAtLeastOne'))
return
}
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
const data = JSON.parse(JSON.stringify(this.realDataSource))
this.selectedRowKeys.forEach(index => {
data[index] = null
})
this.selectedRowKeys = []
this.realDataSource = data.filter(tt => !!tt)
this.$emit('ok', this.realDataSource)
}
})
},
onSelectChange (selectedRowKeys) {
this.selectedRowKeys = selectedRowKeys
},
formModalOk (lineData) {
this.realDataSource.push(lineData)
this.$emit('ok', this.realDataSource)
},
// 点击点击上传按钮
clickButtonToUpload (fileIds, index, dbFieldName) {
this.$refs.uploadFile.open(fileIds)
this.uploadTable = { index, dbFieldName }
},
// 文件上传改变后的回调
uploadFileChange (data) {
console.log(data)
const attIdList = []
if (data && data.length > 0) {
data.map(item => {
attIdList.push(item.id)
})
}
/** 赋值给当前对应的表单文件 */
this.$set(this.realDataSource[this.uploadTable.index], this.uploadTable.dbFieldName, attIdList.join(','))
this.$emit('ok', this.realDataSource)
},
/**
* 校验文件是否上传
* @returns {boolean}
*/
validateTableData() {
if (this.realDataSource.some(tt => !tt.feeData)) {
this.$message.warning(this.$t('businessSupport.standardizationActivity.pleaseUploadPaymentInfo'))
return false
}
return true
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.button-text {
width: 100%;
}
</style>
+116
View File
@@ -0,0 +1,116 @@
<template>
<!-- <div>-->
<!-- <first-stage-card></first-stage-card>-->
<!-- </div>-->
<div class="all-wrapper">
<!-- 检索和预警 -->
<search-and-warning-card class="flex-item search-warning-wrapper"></search-and-warning-card>
<!-- 友情链接 -->
<friend-link-card class="flex-item friend-link-wrapper"></friend-link-card>
<!-- 快捷入口 -->
<quick-enter-card class="flex-item quick-enter-wrapper"></quick-enter-card>
<!-- 待办任务 -->
<to-do-task-card class="flex-item to-do-task-wrapper"></to-do-task-card>
</div>
</template>
<script>
// import FirstStageCard from './modules/FirstStageCard'
import SearchAndWarningCard from './modules/SearchAndWarningCard'
import QuickEnterCard from './modules/QuickEnterCard'
import FriendLinkCard from './modules/FriendLinkCard'
import ToDoTaskCard from './modules/ToDoTaskCard'
export default {
name: 'Analysis',
components: { ToDoTaskCard, FriendLinkCard, QuickEnterCard, SearchAndWarningCard },
// components: { FirstStageCard },
methods: {}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.all-wrapper {
display: flex;
flex-direction: column;
height: calc(100vh - 83px);
flex-wrap: wrap;
}
.flex-item {
margin: 10px;
}
// 检索和预警
.search-warning-wrapper {
width: calc(70% - 20px);
height: calc(100% - 20px - 162px);
}
// 快捷入口
.quick-enter-wrapper {
width: calc(30% - 20px);
height: 306px;
}
// 友情链接
.friend-link-wrapper {
width: calc(70% - 20px);
height: 142px;
}
// 待办任务
.to-do-task-wrapper {
//grid-area: ToDoTask;
width: calc(30% - 20px);
height: calc(100% - 346px);
}
/deep/ .top-wrapper {
display: flex;
align-items: center;
.left-p {
margin-bottom: 0;
font-size: 20px;
font-family: PingFang SC-Medium, PingFang SC, sans-serif;
font-weight: 500;
color: #1D2129;
//line-height: 28px;
.left-icon {
margin-right: 5px;
color: @primary-color;
font-size: 20px;
}
.anticon {
margin-right: 5px;
color: @primary-color;
font-size: 20px;
}
}
.right-p {
margin-bottom: 0;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
font-weight: 400;
color: #86909C;
line-height: 19px;
display: inline-block;
margin-left: auto;
cursor: pointer;
}
}
// 1366屏幕
@media screen and (max-width: 1366px) {
/deep/ .top-wrapper {
.left-p {
font-size: 16px;
.left-icon {
font-size: 16px;
}
.anticon {
font-size: 16px;
}
}
.right-p {
font-size: 12px;
}
}
}
</style>
@@ -0,0 +1,162 @@
<template>
<div class="analysis-box">
<div class="content-box">
<img class="logo-img" src="../../../assets/image/analysisLogo.png"/>
<div class="search-box">
<a-input-group compact class="search-input-group">
<a-select v-model="resourceType" :dropdownMatchSelectWidth="false">
<a-select-option value="all">{{ $t('dashboard.generalSearch.all')}}</a-select-option>
<a-select-option v-for="item in resourceTypeOptions" :key="item.value" :value="item.value">
{{ item.title }}
</a-select-option>
<i slot="suffixIcon" class="iconfont icon-nav-bottom"></i>
</a-select>
<a-input v-model="searchValue" :placeholder="$t('pleaseEnter')" @pressEnter="searchInputEnter">
<i slot="prefix" class="iconfont icon-Searchbar_Search_icon"></i>
<!--<a-icon type="search" />-->
</a-input>
</a-input-group>
<a-button type="primary" @click="toSearchWindow('title')">标题检索</a-button>
<a-button type="primary" @click="toSearchWindow('fullText')">全文检索</a-button>
<a-button type="primary" ghost @click="toAdvancedSearch">高级检索</a-button>
</div>
<p class="tips-p">全球法规标准搜索</p>
</div>
</div>
</template>
<script>
import { ajaxGetDictItems } from '@/api/api'
export default {
name: 'FirstStageCard',
data () {
return {
resourceTypeOptions: [], // 资源类型下拉框数据
resourceType: 'all', // 资源类型
searchValue: '' // 检索内容
}
},
mounted () {
this.initResourceTypeOptions()
},
methods: {
// 获取资源类型下拉框数据
initResourceTypeOptions () {
ajaxGetDictItems('resource_type').then(res => {
if (res.success) {
this.resourceTypeOptions = res.result
}
})
},
// 回车跳标题检索
searchInputEnter () {
const { href } = this.$router.resolve({
path: '/generalSearch',
query: {
type: 'title',
resourceType: this.resourceType,
searchValue: this.searchValue
}
})
window.open(href, '_blank')
},
// 点击跳转到一般检索页
toSearchWindow (type) {
const { href } = this.$router.resolve({
path: '/generalSearch',
query: {
type: type,
resourceType: this.resourceType,
searchValue: this.searchValue
}
})
window.open(href, '_blank')
},
// 点击跳转到高级检索页
toAdvancedSearch () {
const { href } = this.$router.resolve({
path: '/advancedSearch'
})
window.open(href, '_blank')
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.analysis-box {
width: 100%;
height: calc(@page-content-h - 20px);
background-image: url("../../../assets/image/analysisBack.png");
background-repeat: no-repeat;
//background-size: 100% 100%;
background-size: cover;
background-position: center center;
padding-top: calc(@page-content-h * 0.2);
}
.content-box {
width: 70%;
margin: 0 auto;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.logo-img {
width: 222px;
}
.tips-p {
margin-top: 24px;
font-size: 16px;
font-family: PingFang SC, PingFang SC;
font-weight: 400;
color: #86909C;
line-height: 22px;
}
}
.search-box {
width: 100%;
height: 56px;
margin-top: 60px;
display: flex;
gap: 10px;
.search-input-group {
height: 56px;
flex: 1;
display: flex;
/deep/ .ant-select-selection {
height: 100%;
border-color: @primary-color;
color: @primary-color;
.ant-select-selection__rendered {
height: 100%;
line-height: 54px;
margin-right: 38px;
}
.ant-select-arrow-icon {
font-size: 18px;
color: @primary-color;
}
.ant-select-arrow {
margin-top: -9px;
}
}
/deep/ .ant-input-prefix {
color: @primary-color;
left: 18px;
}
/deep/ .ant-input {
height: 56px;
border-color: @primary-color;
padding-left: 40px;
}
}
/deep/ .ant-btn {
height: 56px;
}
}
</style>
@@ -0,0 +1,87 @@
<template>
<a-card :bordered="false">
<a-spin :spinning="loading">
<div class="top-wrapper">
<span class="left-p"><i class="iconfont icon-link left-icon"></i>友情链接</span>
<span class="right-p" @click="moreLink" v-has="'dashboard:friendShipLInk:linkManage'">链接管理<i class="iconfont icon-right"></i></span>
</div>
<div class="content-wrapper">
<div class="content-div" v-for="item in dataSource" :key="item.id" @click="clickLink(item)">
{{ item.name }}
</div>
</div>
</a-spin>
</a-card>
</template>
<script>
import { getAction } from '@/api/manage'
export default {
name: 'FriendLinkCard',
data () {
return {
loading: true,
dataSource: [],
url: {
list: '/sys/lawsFriendlyLinks/list'
}
}
},
mounted () {
this.loadData()
},
methods: {
loadData () {
this.loading = true
getAction(this.url.list, {}).then((res) => {
if (res.success) {
// grounding 1上架 0下架(只显示上架的)
this.dataSource = res.result.filter(item => item.grounding === '1')
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 点击更多链接
moreLink () {
this.$router.push({
path: '/isystem/friendshipLink'
})
},
// 点击链接
clickLink (record) {
const href = record.link
window.open(href)
}
}
}
</script>
<style scoped lang="less">
.content-wrapper {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr;
grid-gap: 20px;
margin-top: 12px;
}
.content-div {
height: 62px;
padding: 16px;
line-height: 30px;
box-shadow: 0px 0px 8px 0px rgba(0,0,0,0.1);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
font-weight: 400;
color: @primary-color;
text-align: center;
background-image: url('../../../assets/image/dashboardLinkBack.png');
cursor: pointer;
}
</style>
@@ -0,0 +1,167 @@
<template>
<j-modal
:title="$t('dashboard.quickEnter.custom')"
:visible="visible"
:width="1000"
switchFullscreen
@ok="handleOk"
:confirmLoading="loading"
@cancel="close"
:cancelText="$t('close')">
<a-spin :spinning="loading">
<a-transfer
class="transfer"
:data-source="dataSource"
:titles="titles"
:show-search="true"
:filter-option="filterOption"
:target-keys="targetKeys"
:row-key="item => item.id"
:render="item => item.name"
:listStyle="listStyle"
:locale="locale"
@change="handleChange"
/>
</a-spin>
</j-modal>
</template>
<script>
import { getQuickEntryList, saveQuickEntryModuleList } from '@api/dashboard'
export default {
name: 'MenuManageModal',
data () {
return {
loading: false,
// 穿梭框两侧样式
listStyle: {
textAlign: 'left',
width: '40%',
height: '100%'
},
visible: false,
// 可选数据和个人展示模块
titles: [this.$t('dashboard.quickEnter.optionModules'), this.$t('dashboard.quickEnter.personalDisplayModule')],
// 可选数据
dataSource: [],
// 已选数据id
targetKeys: [],
// 已勾选的菜单
selectedMenu: [],
// 之前勾选,但是后来没权限的菜单列表
notPermissionMenu: [],
// 打开弹框传入的ids
ids: [],
// 多语言
locale: {
itemUnit: this.$t('dashboard.quickEnter.itemUnit'),
itemsUnit: this.$t('dashboard.quickEnter.itemsUnit'),
notFoundContent: this.$t('dashboard.quickEnter.listEmpty'),
searchPlaceholder: this.$t('dashboard.quickEnter.pleaseEnterSearchContent')
}
}
},
methods: {
/**
* 打开弹框
* @param ids - 已经选择的ids
* @param selectedMenu
*/
show (ids, selectedMenu) {
this.visible = true
// 清空之前的数据,然后重新请求
this.dataSource = []
this.notPermissionMenu = []
// 存储已经勾选的菜单
this.selectedMenu = selectedMenu
this.targetKeys = ids || []
this.ids = ids
this.getMenuList()
},
// 获取菜单列表
getMenuList () {
this.loading = true
getQuickEntryList().then(res => {
if (res.success) {
// 处理之前有权限,后来没有权限
// 没有权限的菜单接口不会返回,但是已经勾选的菜单需要显示出来
const data = res.result || []
// 所有有权限的菜单id
const hasPermissionIds = data.map(item => item.id)
// 获取已选择但没有权限的列表
this.notPermissionMenu = this.selectedMenu.filter(item => !hasPermissionIds.includes(item.id))
this.dataSource = data.concat(this.notPermissionMenu)
} else {
this.$message.warning(res.message)
}
}).finally(() => {
setTimeout(() => {
this.loading = false
}, 1000)
})
},
// 确定
handleOk () {
// 最多选择9个模块
if (this.targetKeys.length > 9) {
this.$message.warning(this.$t('dashboard.quickEnter.maxSelectedNine'))
return
}
// 如果进来的值和提交的值一样不提示操作成功
if (this.notPermissionMenu.length === 0 && this.ids.join(',') === this.targetKeys.join(',')) {
this.close()
return
}
this.loading = true
saveQuickEntryModuleList({
permissionIds: this.targetKeys.join(',')
}).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 关闭
close () {
this.visible = false
},
// 穿梭框改变
handleChange (targetKeys, direction, moveKeys) {
console.log(targetKeys, direction, moveKeys)
this.targetKeys = targetKeys
},
// 搜索过滤
filterOption (inputValue, option) {
return option.name.indexOf(inputValue) > -1
}
}
}
</script>
<style lang="less" scoped>
.transfer {
height: 50vh;
text-align: center;
/deep/ .ant-transfer-operation button {
margin: 20px 0;
border-radius: 50%;
}
/deep/ .ant-transfer-list-header-selected {
display: inline-flex;
flex-direction: row-reverse;
justify-content: space-between;
width: 94%;
.ant-transfer-list-header-title {
position: static;
}
}
}
</style>
@@ -0,0 +1,207 @@
<template>
<a-card :bordered="false">
<a-spin :spinning="loading">
<div class="top-wrapper">
<p class="left-p"><i class="iconfont icon-rukou left-icon"></i>{{ this.$t('dashboard.quickEnter.quickEnter') }}</p>
<p class="right-p" @click="menuManage" v-has="'dashboard:quickEnter:menuManage'"><i class="iconfont icon-settings"></i>{{ this.$t('dashboard.quickEnter.menuManage') }}</p>
</div>
<div class="content-wrapper">
<div class="content-div" v-for="item in dataSource" :key="item.id" @click="handleTo(item.url)">
<img class="icon-img" :src="item.src" :alt="item.name" v-if="item.src">
<!--<i class="iconfont" :class="'icon-' + 'shengchanliuchengguanli'"></i>-->
<a-tooltip placement="top">
<template slot="title">{{item.name}}</template>
<p>{{ item.name }}</p>
</a-tooltip>
</div>
<div class="plus-div" @click="menuManage" v-if="dataSource.length < 9" v-has="'dashboard:quickEnter:menuManage'">
<a-icon type="plus" class="text-primary-color" />
</div>
</div>
</a-spin>
<!-- 菜单管理 -->
<menu-manage-modal ref="menuManageModal" @ok="loadData"/>
</a-card>
</template>
<script>
import MenuManageModal from '@views/dashboard/modules/MenuManageModal'
import { getQuickEntryModuleList } from '@api/dashboard'
// 菜单id或name --> 图标名
const map = [
{ id: '897f80938bcd2157018bcd22d1770001', name: '法规评估库', srcName: 'project-library' },
{ id: '1696430663505027073', name: '企业标准', srcName: 'enterprise-standard' },
{ id: '1701430695719411713', name: '企标复审计划', srcName: 'standard-review-plan' },
{ id: '1699984911627018242', name: '企标宣贯', srcName: 'standard-promotion' },
{ id: '1701402482251210753', name: '企标稽查', srcName: 'standard-audit' },
{ id: '1699610110131494913', name: '企标级别映射', srcName: 'standard-level-mapping' },
{ id: '1701846774438457346', name: '企标计划', srcName: 'standard-plan' },
{ id: '1701129706890174465', name: '企标评审会记录', srcName: 'standard-review-minutes' },
{ id: '1699268154566213634', name: '内部工作组', srcName: 'internal-working-group' },
{ id: '1706216785733353474', name: '动态消息', srcName: 'dynamic-message' },
{ id: '1699621406990196738', name: '友情链接管理', srcName: 'friend-link-management' },
{ id: '1694954872496123906', name: '国内标准', srcName: 'domestic-standards' },
{ id: '1702146447208464385', name: '已发分享', srcName: 'shared-items' },
{ id: '1702153747398770689', name: '已收分享', srcName: 'received-shares' },
{ id: '897f809c8b41f679018b41f679090000', name: '征求意见', srcName: 'soliciting-opinions' },
{ id: '1692098082295779330', name: '我的收藏', srcName: 'my-favorites' },
{ id: '53a9230444d33de28aa11cc108fb1dba', name: '我的消息', srcName: 'my-messages' },
{ id: 'f1cb187abf927c88b89470d08615f5ac', name: '数据字典', srcName: 'data-dictionary' },
{ id: '1691007717915566081', name: '文档拆分', srcName: 'document-splitting' },
{ id: '897f89fa8b07e219018b07e4338f0001', name: '新建流程', srcName: 'new-process' },
{ id: '1692057704023822337', name: '新旧拆分单对比', srcName: 'new-old-split-comparison' },
{ id: '897f80938bcd2157018bcd21573b0000', name: '未符合项', srcName: 'non-compliance-items' },
{ id: '1697502618994315265', name: '标准化活动', srcName: 'standardization-activities' },
{ id: '897f80868b84b221018b84bb4fe20005', name: '标准预警', srcName: 'standard-warning' },
{ id: '1689517320967122945', name: '标签管理', srcName: 'tag-management' },
{ id: '897f89fa8b1c5a5b018b1c5a5b4c0000', name: '流程中心', srcName: 'process-center' },
{ id: '897f809c8b0cf422018b0d1b5d790006', name: '流程配置', srcName: 'process-configuration' },
{ id: '1702153981491265538', name: '浏览记录', srcName: 'browsing-history' },
{ id: '1696420777130401794', name: '海外标准', srcName: 'overseas-standards' },
{ id: '3f915b2769fc80648e92d04e84ca059d', name: '用户管理', srcName: 'user-center' },
{ id: '897f89fa8b130b62018b130b620d0000', name: '联络人管理', srcName: 'contact-management' },
{ id: '897f809c8b5f5aff018b5f5aff0f0000', name: '自定义比对', srcName: 'custom-comparison' },
{ id: '190c2b43bec6a5f7a4194a85db67d96a', name: '角色管理', srcName: 'role-center' },
{ id: '1706587922522857473', name: '资料中心', srcName: 'resource-center' },
{ id: '1701115229728014338', name: '车型项目库', srcName: 'vehicle-project-library' },
{ id: '45c966826eeff4c99b8f8ebfe74511fc', name: '部门管理', srcName: 'department-management' },
{ id: '1704319763516084225', name: '问答库', srcName: 'questions-answers-library' },
{ id: '897f89d78ca41926018ca419266c0000', name: '任务转交', srcName: 'task-transfer' },
{ id: '897f89d78d1beaa3018d1bec513c0001', name: '文档动态', srcName: 'document-dynamics' }
]
export default {
name: 'QuickEnterCard',
components: { MenuManageModal },
data () {
return {
loading: false,
dataSource: [
// { iconName: 'shengchanliuchengguanli', name: '新建流程' },
// { iconName: 'a-moxingkuaisuchaifen1', name: '标准拆分' },
// { iconName: 'shoucang', name: '我的收藏' },
// { iconName: 'a-fagui1', name: '国内法规' },
// { iconName: 'a-moxingkuaisuchaifen1', name: '征求意见' },
// { iconName: 'jiaoseguanli', name: '角色管理' },
// { iconName: 'wenda1', name: '我的问答' }
]
}
},
created () {
this.loadData()
},
methods: {
// 获取快捷入口
loadData () {
this.loading = true
getQuickEntryModuleList().then(res => {
if (res.success) {
const data = res.result || []
this.dataSource = data.map(item => {
// 如果能找到菜单名,就用对应图标
const findMap = map.find(m => m.id === item.id || m.name === item.name)
if (findMap) {
item.src = require(`@/assets/image/dashboard/${findMap.srcName}.png`)
}
return item
})
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 点击快捷入口,跳转对应页面
handleTo (path) {
this.$router.push({ path })
},
// 菜单管理
menuManage () {
const ids = this.dataSource.map(item => item.id)
// 已经选中的菜单(之前有权限,后来没有了,这时接口不会返回没权限的菜单导致取消不了,需要前端处理)
const selectedMenu = this.dataSource.map(item => {
return {
id: item.id,
name: item.name
}
})
this.$refs.menuManageModal.show(ids, selectedMenu)
}
}
}
</script>
<style scoped lang="less">
.content-wrapper {
//display: flex;
//flex-direction: row;
//flex-wrap: wrap;
//justify-content: space-between;
display: grid;
grid-template-columns: repeat(auto-fill, calc((100% - 20px) / 3));
grid-gap: 10px;
grid-auto-flow: row;
margin-top: 10px;
& > div {
//width: 100px;
//height: 100px;
min-height: 50px;
border-radius: 8px 8px 8px 8px;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.content-div {
// (总高度 - 内边距 - 标题 - 上下边距) / 3
height: calc((306px - 48px - 31px - 20px) / 3);
cursor: pointer;
background: rgba(213,44,38,0.08);
.icon-img {
width: 27px;
}
.iconfont {
font-size: 27px;
color: @primary-color;
}
p {
width: 90%;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-bottom: 0;
font-size: 16px;
font-family: PingFang SC, PingFang SC, sans-serif;
font-weight: 400;
color: #000000;
line-height: 28px;
}
}
.plus-div {
// (总高度 - 内边距 - 标题 - 上下边距) / 3
height: calc((306px - 48px - 31px - 20px) / 3);
cursor: pointer;
border: 1px dashed @primary-color;
box-sizing: border-box;
.anticon {
font-size: 28px;
}
}
}
.text-primary-color {
color: @primary-color
}
@media screen and (max-width: 1366px) {
.content-wrapper {
.content-div {
p {
font-size: 14px;
}
}
}
}
</style>
@@ -0,0 +1,405 @@
<template>
<a-card :bordered="false">
<!-- 搜索区域 -->
<div class="search-wrapper">
<a-select class="resource-type-select" v-model="resourceType">
<a-select-option value="all">{{ $t('dashboard.generalSearch.all')}}</a-select-option>
<a-select-option v-for="item in resourceTypeOptions" :key="item.value" :value="item.value" :title="item.title">
{{ item.title }}
</a-select-option>
</a-select>
<a-input v-model="searchValue" :placeholder="$t('pleaseEnter')" @pressEnter="searchInputEnter"></a-input>
<a-button type="primary" @click="toSearchWindow('title')">标题检索</a-button>
<a-button type="primary" @click="toSearchWindow('fullText')">全文检索</a-button>
<a-button type="primary" ghost @click="toAdvancedSearch">高级检索</a-button>
</div>
<!-- 预警区域 -->
<div class="warning-wrapper">
<!-- tab切换 -->
<div class="tab-wrapper">
<div class="tab-div" :class="warningTab === 'newCarType' ? 'curr-tab' : ''" @click="warningTabChange('newCarType')">新车型实施预警</div>
<div class="tab-div" :class="warningTab === 'carInProduction' ? 'curr-tab' : ''" @click="warningTabChange('carInProduction')">在产车实施预警</div>
<div class="tab-div small-tab-div" :class="warningTab === 'enStandardImplement' ? 'curr-tab small-curr-tab' : ''" @click="warningTabChange('enStandardImplement')">最新发布企标</div>
<div class="tab-div small-tab-div" :class="warningTab === 'enStandardPublish' ? 'curr-tab small-curr-tab' : ''" @click="warningTabChange('enStandardPublish')">最新实施企标</div>
</div>
<!--这里的scroll.y必须有后面用媒体查询进行样式覆盖-->
<a-table
ref="table"
size="middle"
:rowKey="(_, index) => index"
class="table"
:scroll="{ x: '100%', y: '40vh' }"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
@change="handleTableChange"
:loading="loading">
<template slot="text" slot-scope="text">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text">{{ text || text === 0 ? text : global.emptyLine }}</div>
</a-tooltip>
</template>
<!-- 标准号标准名称 -->
<template v-slot:standard="text, record">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a v-if="text" class="link-a" @click="detailClick(record)">{{ text }}</a>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
<!-- 企标号企标名称 -->
<template v-slot:enStandard="text, record">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a v-if="text" class="link-a" @click="enStandardDetailClick(record)">{{ text }}</a>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</a-table>
</div>
</a-card>
</template>
<script>
import { ajaxGetDictItems } from '@/api/api'
import { getAction } from '@api/manage'
import { StandardSource } from '@/enums/commonEnums'
const warningColumns = [
{
title: '来源',
align: 'left',
dataIndex: 'source_dictText',
width: 60,
scopedSlots: { customRender: 'text' }
},
{
title: '标准号',
align: 'left',
dataIndex: 'standardNumber',
width: 120,
scopedSlots: { customRender: 'standard' }
},
{
title: '标准名称',
align: 'left',
dataIndex: 'standardName',
width: 180,
scopedSlots: { customRender: 'standard' }
},
{
title: '新车型实施日期',
align: 'left',
dataIndex: 'newModelImplementationDate',
width: 100,
scopedSlots: { customRender: 'text' }
}
]
const enStandardColumns = [
{
title: '企标编号',
align: 'left',
dataIndex: 'standardNumber',
width: 60,
scopedSlots: { customRender: 'enStandard' }
},
{
title: '企标名称',
align: 'left',
dataIndex: 'standardName',
width: 60,
scopedSlots: { customRender: 'enStandard' }
},
{
title: '标准状态',
align: 'left',
dataIndex: 'standardState_dictText',
width: 60,
scopedSlots: { customRender: 'text' }
},
{
title: '发布日期',
align: 'left',
dataIndex: 'releaseDate',
width: 60,
scopedSlots: { customRender: 'text' }
},
{
title: '实施日期',
align: 'left',
dataIndex: 'implementationDate',
width: 60,
scopedSlots: { customRender: 'text' }
}
]
export default {
name: 'SearchAndWarningCard',
data () {
return {
resourceTypeOptions: [], // 资源类型下拉框数据
resourceType: 'all', // 资源类型
searchValue: '', // 检索内容
warningTab: 'newCarType', // 预警当前tab
loading: false,
columns: [
{
title: this.$t('serialNumber'),
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{
title: '来源',
align: 'left',
dataIndex: 'source_dictText',
width: 60,
scopedSlots: { customRender: 'text' }
},
{
title: '标准号',
align: 'left',
dataIndex: 'standardNumber',
width: 120,
scopedSlots: { customRender: 'standard' }
},
{
title: '标准名称',
align: 'left',
dataIndex: 'standardName',
width: 180,
scopedSlots: { customRender: 'standard' }
},
{
title: '新车型实施日期',
align: 'left',
dataIndex: 'newModelImplementationDate',
width: 100,
scopedSlots: { customRender: 'text' }
}
],
dataSource: [],
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
url: {
newModelWarningList: '/laws/localTool/standardEarlyWarning/newModelImplementation',
inProductionWarningList: '/laws/localTool/standardEarlyWarning/onTheProduction',
enStandardImplementList: '/laws/localTool/standardEarlyWarning/latestImplementEs',
enStandardPublishList: '/laws/localTool/standardEarlyWarning/latestReleaseEs'
}
}
},
mounted () {
this.initResourceTypeOptions()
this.loadData(1)
},
methods: {
// 获取资源类型下拉框数据
initResourceTypeOptions () {
ajaxGetDictItems('resource_type').then(res => {
if (res.success) {
this.resourceTypeOptions = res.result
}
})
},
// 回车跳标题检索
searchInputEnter () {
const { href } = this.$router.resolve({
path: '/generalSearch',
query: {
type: 'title',
resourceType: this.resourceType,
searchValue: this.searchValue
}
})
window.open(href, '_blank')
},
// 点击跳转到一般检索页
toSearchWindow (type) {
const { href } = this.$router.resolve({
path: '/generalSearch',
query: {
type: type,
resourceType: this.resourceType,
searchValue: this.searchValue
}
})
window.open(href, '_blank')
},
// 点击跳转到高级检索页
toAdvancedSearch () {
const { href } = this.$router.resolve({
path: '/advancedSearch'
})
window.open(href, '_blank')
},
// 预警tab切换
warningTabChange (value) {
this.warningTab = value
this.dataSource = []
if (value === 'newCarType') {
this.columns.splice(1, this.columns.length - 1, ...warningColumns)
this.columns[4].title = '新车型实施日期'
this.columns[4].dataIndex = 'newModelImplementationDate'
} else if (value === 'carInProduction') {
this.columns.splice(1, this.columns.length - 1, ...warningColumns)
this.columns[4].title = '在产车实施日期'
this.columns[4].dataIndex = 'onTheProductionDate'
} else {
// 是企标实施预警或企标发布预警
this.columns.splice(1, this.columns.length - 1, ...enStandardColumns)
}
this.loadData(1)
},
// 跳转详情
detailClick (record) {
let path
// 根据来源跳转不同的详情页
switch (record.source + '') {
// 国内
case StandardSource.DOMESTIC.value:
path = '/standardRegulationLibrary/DomesticStandardDetail'
break
// 海外
case StandardSource.OVERSEAS.value:
path = '/standardRegulationLibrary/OverseasStandardDetail'
break
// 企标
case StandardSource.ENTERPRISE.value:
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
break
default:
break
}
this.$openPageNewSheet({
path,
query: {
id: record.id
}
})
},
// 企标详情
enStandardDetailClick (record) {
this.$openPageNewSheet({
path: '/enterpriseStandardLibrary/enterpriseStandardDetail',
query: {
id: record.id
}
})
},
handleTableChange (pagination) {
this.ipagination = pagination
this.loadData()
},
loadData (arg) {
// 加载数据 若传入参数1则加载第一页的内容
if (arg === 1) {
this.ipagination.current = 1
}
const params = {}// 查询条件
params.pageNo = this.ipagination.current
params.pageSize = this.ipagination.pageSize
params.columns = 'createTime'
params.order = 'desc'
this.loading = true
let url = ''
if (this.warningTab === 'newCarType') {
url = this.url.newModelWarningList
} else if (this.warningTab === 'carInProduction') {
url = this.url.inProductionWarningList
} else if (this.warningTab === 'enStandardImplement') {
url = this.url.enStandardImplementList
} else {
url = this.url.enStandardPublishList
}
getAction(url, params).then((res) => {
if (res.success) {
this.dataSource = res.result.records || res.result
if (res.result.total) {
this.ipagination.total = res.result.total
} else {
this.ipagination.total = 0
}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
}
}
}
</script>
<style scoped lang="less">
.search-wrapper {
display: grid;
grid-gap: 10px;
grid-template-columns: 96px 200px 88px 88px 88px;
}
.resource-type-select {
width: 96px;
}
// 预警容器
.warning-wrapper {
padding: 24px 0;
}
// 预警tab切换
.tab-wrapper {
width: 664px;
display: flex;
justify-content: space-between;
align-items: center;
}
.tab-div {
cursor: pointer;
width: 176px;
height: 32px;
background: rgba(245, 245, 245, 0.60);
color: #4E5969;
text-align: center;
line-height: 32px;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
font-weight: 400;
}
.small-tab-div {
width: 156px;
}
.curr-tab {
width: 176px;
height: 44px;
line-height: 42px;
border-top: 2px solid @primary-color;
color: @primary-color;
box-sizing: border-box;
font-weight: 500;
//transition-property: width,height,border-top;
//transition-duration: 0.1s;
//transition-timing-function: ease-in;
}
.small-curr-tab {
width: 156px;
}
// 通过媒体查询覆盖表格高度
.table /deep/ .ant-table-scroll .ant-table-body {
max-height: calc(100vh - 20px - 162px - 310px) !important;
}
</style>
@@ -0,0 +1,214 @@
<template>
<a-card :bordered="false">
<a-spin :spinning="loading">
<div class="top-wrapper">
<p class="left-p"><i class="iconfont icon-calendar left-icon"></i>{{ $t('dashboard.todoTask.todoTask') }}</p>
<p class="right-p" @click="handleMore">{{ $t('dashboard.todoTask.more') }}<i class="iconfont icon-right"></i></p>
</div>
<div class="content-wrapper" v-if="dataSource.length > 0">
<div class="content-item" v-for="item in dataSource" :key="item.id" @click="handleToDetail(item)">
<a-tooltip placement="top">
<template slot="title">{{item.prcName}}</template>
<p>{{ item.prcName }}</p>
</a-tooltip>
</div>
</div>
<a-list :data-source="[]" v-else></a-list>
</a-spin>
</a-card>
</template>
<script>
import { getTodoList } from '@api/dashboard'
export default {
name: 'ToDoTaskCard',
data () {
return {
loading: false,
// 数据
dataSource: []
}
},
created () {
this.getTodoTask()
},
methods: {
// 获取待办任务
getTodoTask () {
this.loading = true
getTodoList().then(res => {
if (res.success) {
this.dataSource = res.result.records || []
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 跳转待办详情
handleToDetail (record) {
let path = ''
// 跳转到对应流程页面
switch (record.prcType + '') {
// 标准法规入库/变更流程
case '1':
path = '/workCenter/StandardEntryOrChangeProcess'
break
// 标准征求意见流程
case '2':
path = '/workCenter/StandardConsultationProcess'
break
// 项目合规评估流程
case '3':
path = '/workCenter/ProjectComplianceEvaluationProcess'
break
// 未符合项跟踪流程
case '4':
path = '/workCenter/NoMatchTrackingProcess'
break
// 法规合规评估流程
case '5':
path = '/workCenter/StandardComplianceProcess'
break
// 法规采购流程
case '6':
path = '/workCenter/StandardProcurementProcess'
break
// 企标立项、变更计划
case '7':
path = '/workCenter/enterpriseStandardInitChange/planApprovalChangeFlow'
break
// 年度制修订计划(各部门主动上报)
case '8':
path = '/workCenter/annualRevisionPlanActivelyReport/revisionPlanActivelyReportFlow'
break
// 年度制修订计划(标准化发起)
case '9':
path = '/workCenter/annualRevisionPlanStandardizationInit/revisionPlanStandardizationInitFlow'
break
// 企标制修订流程
case '10':
path = '/workCenter/enterpriseStandardRevision/enterpriseStandardRevisionFlow'
break
// 企标更改流程
case '11':
path = '/workCenter/enterpriseStandardChange/enterpriseStandardChangeFlow'
break
// 企标复审流程
case '12':
path = '/workCenter/enterpriseStandardRecheck/enterpriseStandardRecheckFlow'
break
// 企标废止流程
case '13':
path = '/workCenter/enterpriseStandardAnnul/enterpriseStandardAnnulFlow'
break
// 企标封存流程
case '14':
path = '/workCenter/enterpriseStandardSealSave/enterpriseStandardSealSaveFlow'
break
// 已封存企标启用流程
case '15':
path = '/workCenter/enterpriseStandardStartUse/enterpriseStandardStartUseFlow'
break
// 企标稽查计划
case '16':
path = '/workCenter/EnterpriseStandardInspectionPlan'
break
// 企标稽查流程
case '17':
path = '/workCenter/EnterpriseStandardInspectionProcess'
break
// 稽查延期申请流程
case '18':
path = '/workCenter/InspectionExtensionRequestProcess'
break
// 企标稽查整改
case '19':
path = '/workCenter/EnterpriseStandardInspectionRectification'
break
// 权限申请流程
case '20':
path = '/workCenter/PermissionApplicationProcess'
break
// 标准宣贯流程
case '21':
path = '/workCenter/StandardPromotionProcess'
break
// 会后管理流程
case '22':
path = '/workCenter/PostMeetingManageProcess'
break
// 如果都没找到不用跳转
default:
return
}
this.$router.push({
path: path,
query: {
projectId: record.projectId,
taskName: record.taskName,
taskId: record.taskId, // 任务id
processInstanceId: record.processInstanceId, // 流程实例id,必须传人员信息右侧表格查询需要用
nodeId: record.nodeId // 节点的id
}
})
},
// 更多:跳转待办
handleMore () {
this.$router.push({
path: '/workCenter/processCenter/processCenter'
})
}
}
}
</script>
<style lang="less" scoped>
.content-wrapper {
margin-top: 10px;
height: calc(100vh - 346px - 200px);
overflow-y: auto;
.content-item {
cursor: pointer;
word-break: break-all;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-family: PingFang SC, PingFang SC, sans-serif;
font-weight: 400;
color: #1D2129;
position: relative;
font-size: 16px;
line-height: 48px;
height: 48px;
border-bottom: 1px solid #E5E6EB;
text-indent: 18px;
}
.content-item::before {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
display: block;
content: '';
background-color: #86909C;
width: 6px;
height: 6px;
border-radius: 50%;
}
}
@media screen and (max-width: 1366px) {
.content-wrapper {
height: calc(100vh - 306px - 200px);
.content-item {
line-height: 40px;
height: 40px;
font-size: 14px;
}
}
}
</style>
@@ -0,0 +1,682 @@
<template>
<a-card :bordered="false">
<a-spin :spinning="loading">
<!-- 一般检索和高级检索的tab切换 -->
<div class="tab-wrapper">
<span class="tab-title" @click="clickGeneralSearch">一般检索</span>
<span class="tab-title tab-title-curr">高级检索</span>
<a @click="handleToggleChange">{{ toggleSearchStatus ? '收起条件' : '展开条件' }}</a>
</div>
<a-divider>检索类别</a-divider>
<div class="filter-area-div" ref="filterArea">
<a-row :gutter="24" class="filter-row">
<a-col :md="2" class="label-col"><span class="search_label">标准类型</span></a-col>
<a-col :md="18" class="option-col">
<div v-for="item in standardTypeOptions" :key="item.value" class="option-div" :class="currStandardType === item.value ? 'option-curr' : ''" @click="standardTypeChange(item.value)">
{{ item.label }}
</div>
</a-col>
</a-row>
</div>
<a-form v-if="toggleSearchStatus" layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="(24 / (minLength + 1))"
:sm="(24 / minLength)"
v-for="(item) in searchList"
:key="item.id"
style="line-height: 48px">
<!-- 树形选择器 -->
<a-form-item :label="item.dbFieldTxt" :labelCol="labelCol" :wrapperCol="wrapperCol" :colon="false">
<template v-if="item.fieldShowType === FieldType.TREE.value">
<a-tree-select
tree-node-filter-prop="title"
v-model="queryParam[item.dbFieldName]"
:maxTagCount="1"
:getPopupContainer="triggerNode=> triggerNode.parentNode"
:tree-data="optionsData[item.dbFieldName]"
tree-checkable
:placeholder="$t('pleaseSelect')+item.dbFieldTxt"
treeCheckStrictly
/>
</template>
<!-- 树形选择器单选 -->
<template v-if="item.fieldShowType === FieldType.TREE_SINGLE.value">
<a-tree-select
tree-node-filter-prop="title"
v-model="queryParam[item.dbFieldName]"
:maxTagCount="1"
:getPopupContainer="triggerNode=> triggerNode.parentNode"
:tree-data="optionsData[item.dbFieldName]"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt"
/>
</template>
<!-- 输入框 -->
<template
v-if="[FieldType.TEXT_BOX.value, FieldType.TEXTAREA.value,FieldType.USER_SINGLE.value,
FieldType.STANDARD_MORE.value,FieldType.TREE_MODAL_SELECT.value]
.includes(item.fieldShowType)">
<a-input :placeholder="$t('pleaseEnter')+item.dbFieldTxt"
v-model="queryParam[item.dbFieldName]"></a-input>
</template>
<!-- 日期区间 -->
<template v-else-if="[FieldType.DATE_SINGLE.value, FieldType.DATE_MORE.value].includes(item.fieldShowType)">
<a-range-picker v-model="queryParam[item.dbFieldName]"
format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@change="onChange(item.dbFieldName)"></a-range-picker>
</template>
<!-- 字典单选框 -->
<template v-else-if="item.fieldShowType === FieldType.OPTION_SINGLE.value">
<j-dict-select-tag v-model="queryParam[item.dbFieldName]"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt"
:type="'select'"
:triggerChange="false" :dictCode="item.dictField" />
</template>
<!-- 字典多选框 -->
<template v-else-if="item.fieldShowType === FieldType.OPTION_MORE.value">
<j-multi-select-tag v-model="queryParam[item.dbFieldName]"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt"
:type="'select'" :maxTagCount="1"
:triggerChange="false" :dictCode="item.dictField"/>
</template>
<!-- 组织机构选择 -->
<template v-else-if="item.fieldShowType === FieldType.ORGAN_SINGLE.value">
<a-tree-select
tree-node-filter-prop="title"
v-model="queryParam[item.dbFieldName]"
:maxTagCount="1"
:show-search="true"
allowClear
:getPopupContainer="triggerNode=> triggerNode.parentNode"
class="box-input"
style="width: 100%"
:tree-data="categoryTreeList"
:placeholder="$t('pleaseSelect') + item.dbFieldTxt"
/>
</template>
<!-- 组织机构多选 -->
<template v-else-if="item.fieldShowType === FieldType.ORGAN_MORE.value">
<a-tree-select
tree-node-filter-prop="title"
v-model="queryParam[item.dbFieldName]"
:maxTagCount="1"
:show-search="true"
allowClear
:getPopupContainer="triggerNode=> triggerNode.parentNode"
class="box-input"
style="width: 100%"
:tree-data="categoryTreeList"
:placeholder="$t('pleaseSelect') + item.dbFieldTxt"
treeCheckable
treeCheckStrictly
/>
</template>
<!-- 年份选择 -->
<template v-else-if="item.fieldShowType === FieldType.YEAR_PICKER.value">
<j-date show-type="year"
:allowClear="false"
v-model="queryParam[item.dbFieldName]"
date-format="YYYY"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt" />
</template>
</a-form-item>
</a-col>
</a-row>
</a-form>
<div class="table-page-search-submitButtons">
<a-button icon="search" type="primary" @click="searchQuery" :loading="loading" v-has="'dashboard:advancedSearch:search'">{{ $t('query') }}</a-button>
<a-button icon="reload" type="primary" ghost style="margin-left: 8px" @click="searchReset" :loading="loading">{{ $t('reset') }}</a-button>
</div>
<div class="table-wrapper">
<j-table
:can-drag="true"
:scroll="{x: '100%'}"
ref="table"
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
@change="handleTableChange">
<template v-slot:toDetail="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a v-if="text && ( record.detailFlag === undefined || record.detailFlag === true )" class="link-a" @click="filePreview(record)">{{ text }}</a>
<div v-else-if="text && record.detailFlag === false" class="table-text">{{ text }}</div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</j-table>
</div>
</a-spin>
</a-card>
</template>
<script>
import { filterObj } from '@/utils/util'
import { StandardSource, FieldType, EnterpriseStandardStatus, StandardStatus } from '@/enums/commonEnums'
import { getAction } from '@/api/manage'
import { getFormDictTreeList, queryDepartTreeList, getFileInfo } from '@/api/api'
import {
getDomesticStandardList,
getOverseasStandardList,
getEnterStandardList,
getDomesticStandardQuery,
getOverseasStandardQuery,
getEnterStandardQuery
} from '@/api/dashboard'
import JTable from '@/components/jero/JTable'
import {
getDomesticStandardFormModal,
getDomesticStandardDetail,
getOverseasStandardForm,
getOverseasStandardDocumentInfo
} from '../../../api/standardRegulationLibraryApi'
import { getEnterpriseStandardAddForm, getEnterpriseStandardInfoById } from '../../../api/enterpriseStandardLibraryApi'
import Vue from 'vue'
import { ACCESS_TOKEN } from '../../../store/mutation-types'
import { previewPdf } from '../../../utils/previewPdf'
import { kkFilePreview } from '../../../utils/kkFilePreview'
export default {
name: 'AdvancedSearch',
components: { JTable },
data () {
return {
FieldType,
loading: false,
toggleSearchStatus: true, // 检索条件的展开和收起,false收缩状态
currStandardType: '1',
standardTypeOptions: [
{ label: '国内标准法规', value: StandardSource.DOMESTIC.value },
{ label: '海外标准法规', value: StandardSource.OVERSEAS.value },
{ label: '企业标准', value: StandardSource.ENTERPRISE.value }
],
labelCol: {
xl: 8,
xxl: 6
},
wrapperCol: {
span: 14
},
getQueryConditionFunc: undefined, // 获取查询条件列表
getDataListFunc: undefined, // 获取数据列表
minLength: 2,
searchList: [], // 搜索条件
optionsData: [], // 搜索条件下拉数据
categoryTreeList: [], // 组织机构下拉框数据
queryParam: {},
columns: [
{ // 标准号
title: this.$t('dashboard.advancedSearch.standardNum'),
align: 'center',
width: 180,
dataIndex: 'standard_number',
scopedSlots: { customRender: 'toDetail' }
},
{ // 标准名称
title: this.$t('dashboard.advancedSearch.standardName'),
align: 'center',
width: 180,
dataIndex: 'standard_name',
scopedSlots: { customRender: 'toDetail' }
},
{ // 发布日期
title: this.$t('dashboard.advancedSearch.releaseDate'),
align: 'center',
width: 180,
dataIndex: 'release_date',
scopedSlots: { customRender: 'text' }
},
{ // 实施日期
title: this.$t('dashboard.advancedSearch.materialDate'),
align: 'center',
width: 180,
dataIndex: 'implementation_date',
scopedSlots: { customRender: 'text' }
},
{ // 标准状态
title: this.$t('dashboard.advancedSearch.standardStatus'),
align: 'center',
width: 180,
dataIndex: 'standard_state_dictText',
scopedSlots: { customRender: 'text' }
}
],
dataSource: [],
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
getFormFunc: getDomesticStandardFormModal,
getDocumentInfoFunc: getDomesticStandardDetail,
// 国标和外标的默认标准状态
defaultStandardStatus: `${StandardStatus.CURRENTLY_EFFECTIVE.value},${StandardStatus.BE_IMPLEMENTED_SOON.value},${StandardStatus.RELEASE.value}`,
// 企标的默认标准状态
defaultEnStandardStatus: `${EnterpriseStandardStatus.CURRENTLY_EFFECTIVE.value},${EnterpriseStandardStatus.TRIAL_OPERATION.value},${EnterpriseStandardStatus.RELEASE.value}`
}
},
watch: {
currStandardType: {
immediate: true,
handler (val) {
if (val === '1') {
this.getQueryConditionFunc = getDomesticStandardQuery
this.getDataListFunc = getDomesticStandardList
this.getFormFunc = getDomesticStandardFormModal
this.getDocumentInfoFunc = getDomesticStandardDetail
} else if (val === '2') {
this.getQueryConditionFunc = getOverseasStandardQuery
this.getDataListFunc = getOverseasStandardList
this.getFormFunc = getOverseasStandardForm
this.getDocumentInfoFunc = getOverseasStandardDocumentInfo
} else if (val === '3') {
this.getQueryConditionFunc = getEnterStandardQuery
this.getDataListFunc = getEnterStandardList
this.getFormFunc = getEnterpriseStandardAddForm
this.getDocumentInfoFunc = getEnterpriseStandardInfoById
}
this.getSearch()
// this.loadData(1)
}
}
},
mounted () {
this.getQueryConditionFunc = getDomesticStandardQuery
this.getSearch()
this.getSysCategoryTree()
this.queryParam.standard_state = this.defaultStandardStatus
},
methods: {
// 点击一般检索
clickGeneralSearch () {
this.$router.push({
path: '/generalSearch'
})
},
// 跳转详情
handleGoDetail (record) {
let path = ''
// 需要先判断是哪种标准
if (this.currStandardType === '1') {
path = '/standardRegulationLibrary/DomesticStandardDetail'
} else if (this.currStandardType === '2') {
path = '/standardRegulationLibrary/OverseasStandardDetail'
} else if (this.currStandardType === '3') {
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
}
this.$openPageNewSheet({
path: path,
query: {
id: record.id
}
})
},
/**
* 按顺序打开最新的pdf或word
*/
async filePreview (record) {
console.log('filePreview', record)
// 获取并处理过程稿件数据
let res = await this.getFormFunc({ type: 1 })
if (!res.success) {
this.$message.warn(res.message)
console.log('getFormFunc error:', res)
return
}
console.log('res.result', res.result)
const data = res.result || {}
const form = data
const partList = Object.keys(data)
let part
const partZh = '过程稿件'
const partEn = 'Process Manuscript'
if (partList.includes(partEn))part = partEn
if (partList.includes(partZh))part = partZh
console.log('part', part)
const processManuscript = form[part]
console.log('过程稿件', processManuscript)
// 获取并处理详情数据
res = await this.getDocumentInfoFunc({ id: record.id, type: 1 })
if (!res.success) {
this.$message.warn(res.message)
console.log('getDocumentInfoFunc error:', res)
return
}
const detailData = Object.assign({}, res.result || {})
console.log('detailData', detailData)
// 选择合适的文件
let file = null
const pdfFileList = []
const docFileList = []
let newestFileList = []
let manuscriptName = '' // 记录找到哪个稿件就找到文件了
// 按照过程稿件顺序寻找文件
for (const formItem of processManuscript) {
const keyId = formItem.dbFieldName
const keyName = formItem.dbFieldName + '_dictText'
if (!detailData[keyId]) continue
const idArray = detailData[keyId].split(',')
const nameArray = detailData[keyName].split(',')
// 每个过程中可能有多个文件
for (let i = 0; i < idArray.length; i++) {
const id = idArray[i]
const name = nameArray[i]
var suffix = name.substring(name.lastIndexOf('.') + 1).toLowerCase()
if (suffix === 'pdf') {
pdfFileList.push({ id, name, suffix })
}
if (suffix === 'docx') {
docFileList.push({ id, name, suffix })
}
}
// 一旦一个过程中找到pdf或者doc,就可以退出
if (pdfFileList.length > 0 || docFileList.length > 0) {
manuscriptName = formItem.dbFieldName
break
}
}
if (manuscriptName === 'publish_of_original' && this.currStandardType === StandardSource.ENTERPRISE.value) {
// 是企标,在发布稿就找到了,合并pdf和doc文件列表排序
newestFileList = [...pdfFileList, ...docFileList]
} else {
newestFileList = pdfFileList // 默认从pdf里选最新的文件
if (pdfFileList.length === 0)newestFileList = docFileList // 如果没有pdf文件,就从doc文件里选
}
file = await this.selectNewestFile(newestFileList)
if (file === null) { // 没有找到文件就退出
console.log('没有找到符合要求的文件!')
this.$message.error('该标准暂无预览文件,请联系标准管理员。')
return
}
console.log('seletedFile', file)
// 是onlyOffice生成的,预览关联的pdf文件
if (file.pdfId) {
// 说明是onlyOffice生成的
const url = previewPdf(file.pdfId)
window.open(url)
return
}
// 预览选择的文件
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.name}`
console.log(fileFullUrl)
if (file.suffix === 'pdf') { // pdf预览
const url = previewPdf(file.id)
window.open(url)
return
}
kkFilePreview(fileFullUrl)// 其余可预览文件仍使用KKFile进行预览
},
/**
* 从文件列表里选择最新文件
* @param fileList
*/
async selectNewestFile (fileList) {
if (!fileList || fileList.length === 0) return null
if (fileList.length === 1) {
const res = await getFileInfo(fileList[0])
return { ...fileList[0], ...res.result }
}
const newFileList = []
// 获取时间信息
for (const file of fileList) {
const res = await getFileInfo(file)
newFileList.push({ ...file, ...res.result })
}
// 排序取出最新
newFileList.sort((a, b) => b.id - a.id) // 日期只精确到天,用id更能区分
console.log('newFileList', newFileList)
return newFileList[0]
},
// 标准类型改变
standardTypeChange (value) {
this.currStandardType = value
this.queryParam = {}
this.dataSource = []
this.ipagination = {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
}
if (value === StandardSource.ENTERPRISE.value) {
this.queryParam.standard_state = this.defaultEnStandardStatus
} else {
this.queryParam.standard_state = this.defaultStandardStatus
}
},
onChange (item) {
if (this.queryParam[item] && this.queryParam[item].length > 0) {
const startDate = item + '_start'
const endDate = item + '_end'
this.queryParam[startDate] = this.queryParam[item][0]
this.queryParam[endDate] = this.queryParam[item][1]
} else {
this.queryParam[item] = []
}
},
// 获取组织机构树
getSysCategoryTree () {
queryDepartTreeList().then((res) => {
if (res.success) {
this.categoryTreeList = res.result
} else {
this.categoryTreeList = []
}
})
},
// 获取查询条件列表
getSearch () {
if (!this.getQueryConditionFunc || typeof this.getQueryConditionFunc !== 'function') {
return
}
const params = {
module: this.currStandardType
}
this.loading = true
this.getQueryConditionFunc(params).then((res) => {
if (res.success) {
this.searchList = res.result
// 标准状态特殊处理成多选
const standardStateIndex = this.searchList.findIndex(item => item.dbFieldName === 'standard_state')
this.searchList[standardStateIndex].fieldShowType = FieldType.OPTION_MORE.value
this.getOptionsData() // 获取下拉数据
}
}).finally(() => {
this.loading = false
})
},
/**
* 处理需要通过接口获取的下拉数据
*/
getOptionsData () {
const formList = Object.values(this.searchList).reduce((acc, cur) => acc.concat(cur), [])
formList.forEach(item => {
if (item.fieldShowType === FieldType.TREE.value || item.fieldShowType === FieldType.TREE_SINGLE.value) {
// 展示类型是树选择,且有接口的
if (item.fieldHref) {
getAction(item.fieldHref).then(res => {
if (res.success) {
this.$set(this.optionsData, item.dbFieldName, res.result)
}
})
} else if (item.dictId) {
getFormDictTreeList({ dictId: item.dictId }).then(res => {
if (res.success) {
this.$set(this.optionsData, item.dbFieldName, res.result)
}
})
}
}
})
},
getQueryParams () {
const formInline = JSON.parse(JSON.stringify(this.queryParam))
Object.keys(formInline).forEach(res => {
if (formInline[res] instanceof Array) {
if (formInline[res][0] instanceof Object) {
// 说明是树选择
formInline[res] = formInline[res].map(item => item.value).join(',')
} else {
formInline[res] = formInline[res].join(',')
}
}
})
formInline.pageNo = this.ipagination.current
formInline.pageSize = this.ipagination.pageSize
const param = Object.assign(formInline)
return filterObj(param)
},
loadData (arg) {
if (arg === 1) {
this.ipagination.current = 1
}
const params = this.getQueryParams()// 查询条件
this.loading = true
this.getDataListFunc(params).then((res) => {
if (res.success) {
this.dataSource = res.result.records || res.result
if (res.result.total) {
this.ipagination.total = res.result.total
} else {
this.ipagination.total = 0
}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 收齐展开点击
handleToggleChange () {
this.toggleSearchStatus = !this.toggleSearchStatus
},
handleTableChange (pagination, filters, sorter) {
// 分页、排序、筛选变化时触发
if (Object.keys(sorter).length > 0) {
this.isorter.column = sorter.order ? sorter.field : 'createTime'
this.isorter.order = sorter.order === 'ascend' ? 'asc' : 'desc'
}
this.ipagination = pagination
this.loadData()
},
// 查询
searchQuery () {
const flag = Object.values(this.queryParam).some(v => !!v || v === 0)
if (!flag) {
// 提示至少填写一项
this.$message.warning(this.$t('pleaseFillInAtLeastOneItem'))
return
}
this.loadData(1)
},
// 重置
searchReset () {
this.queryParam = {}
if (this.currStandardType === StandardSource.ENTERPRISE.value) {
this.queryParam.standard_state = this.defaultEnStandardStatus
} else {
this.queryParam.standard_state = this.defaultStandardStatus
}
this.dataSource = []
this.ipagination.total = 0
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.tab-wrapper {
span {
margin-right: 20px;
cursor: pointer;
}
.tab-title {
font-size: 20px;
}
.tab-title-curr {
color: @primary-color;
}
}
// 筛选区域
.filter-area-div {
padding: 10px 20px;
box-sizing: border-box;
.filter-row {
height: auto;
line-height: 44px;
display: flex;
}
.label-col {
text-align: left;
color: #86909c;
}
.option-col {
width: calc(100% - 200px);
height: auto;
color: #1D2129;
display: flex;
align-items: center;
flex-wrap: wrap;
.option-div {
width: auto;
height: 32px;
line-height: 32px;
padding: 0 12px;
cursor: pointer;
margin: 6px 4px;
overflow: hidden;
white-space: nowrap;
}
}
}
// 选中项样式
.option-curr {
background: rgba(213, 44, 38, 0.08);
color: @primary-color;
border-radius: 4px 4px 4px 4px;
}
/deep/ .ant-form {
padding: 0 20px;
}
/deep/ .ant-form-item {
width: 100%;
}
/deep/ .ant-form-item-label {
text-align: left;
label {
color: #86909c !important;
}
}
.table-page-search-submitButtons {
text-align: center;
margin-top: 10px;
}
.table-wrapper {
margin-top: 20px;
}
</style>
@@ -0,0 +1,937 @@
<template>
<a-spin :spinning="loading || selectOptionLoading">
<a-card :bordered="false" :bodyStyle="{height: cardHeight}">
<!-- 搜索框部分 -->
<div class="top-search-div">
<div class="top-search-div-content">
<a-input-search class="input-search"
:class="!isHasPermission('dashboard:generalSearch:search') ? 'search-no-btn' : ''"
:placeholder="$t('pleaseEnter')+$t('dashboard.generalSearch.queryCondition')"
v-model="searchValue"
:maxLength="50"
@search="onInputSearch()">
<a-button v-has="'dashboard:generalSearch:search'" type="primary" slot="enterButton">
<i class="iconfont icon-Searchbar_Search_icon"></i>
</a-button>
</a-input-search>
<!-- 高级检索 -->
<a class="search-link" @click="clickAdvancedSearch">{{ $t('dashboard.generalSearch.advancedSearch') }}</a>
<!-- 首页 -->
<a class="search-link" @click="clickHomePage">{{ $t('dashboard.generalSearch.homePage') }}</a>
<!-- 检索历史 -->
<a class="search-link" @click="clickSearchHistory" v-has="'dashboard:generalSearch:searchHistory'">{{ $t('dashboard.generalSearch.searchHistory') }}</a>
</div>
</div>
<!-- 筛选区域 -->
<div class="filter-area-div" ref="filterArea">
<!-- 检索方式 -->
<a-row :gutter="24" class="filter-row">
<a-col :md="6" class="label-col"><span class="search_label">{{ $t('dashboard.generalSearch.retrievalMode') }}</span></a-col>
<a-col :md="18" class="option-col">
<div v-for="item in retrievalModeOptions" :key="item.key" class="option-div" :class="retrievalModeCurrent === item.key ? 'option-curr' : ''" @click="retrievalModeChange(item.key)">
{{ item.label }}
</div>
</a-col>
</a-row>
<!-- 资源类型 -->
<a-row :gutter="24" class="filter-row">
<a-col :md="6" class="label-col"><span class="search_label">{{ $t('dashboard.generalSearch.resourceType') }}</span></a-col>
<a-col :md="18" class="option-col">
<div v-for="item in resourceTypeOptions" :key="item.key" class="option-div" :class="resourceTypeCurrent === item.key ? 'option-curr' : ''" @click="resourceTypeChange(item.key)">
{{ item.label }}
</div>
</a-col>
</a-row>
<!-- 标准状态设置 -->
<a-row :gutter="24" class="filter-row">
<a-col :md="6" class="label-col"><span class="search_label">{{ $t('dashboard.generalSearch.standardStatusSet') }}</span></a-col>
<a-col :md="18" class="option-col">
<div v-for="item in standardStatusSetOptions" :key="item.key" class="option-div" :class="standardStatusSetCurrent === item.key ? 'option-curr' : ''" @click="standardStatusSetChange(item.key)">
{{ item.label }}
</div>
</a-col>
</a-row>
<!-- 其他筛选条件 -->
<template v-if="toggleSearchStatus">
<template v-for="(value, key) in selectOptionsKeys">
<div :key="key" v-if="selectOptions[value]" :gutter="24" class="filter-row">
<div class="label-col"><span class="search_label">{{ selectOptions[value].label }}</span></div>
<div class="option-col">
<div v-for="item in selectOptions[value].options" :key="item.itemValue" class="option-div" :class="selectOptions[value].current === item.itemValue ? 'option-curr' : ''" @click="selectOptionsChange(value, item.itemValue)">
<span>{{ item.itemText }}<span v-if="item.statisticsCount || item.statisticsCount === 0">(<span class="option-span-num">{{ item.statisticsCount }}</span>)</span></span>
</div>
</div>
</div>
</template>
</template>
</div>
<div class="toggle-div" @click="handleToggleChange" v-if="selectOptions && Object.keys(selectOptions).length !== 0">
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
{{ toggleSearchStatus ? $t('putAway') : $t('open') }}
</div>
<!-- 结果展示 -->
<template v-if="dataSource && dataSource.length > 0">
<!-- 查询结果展示区域 -->
<div class="result-wrapper">
<div class="result-div" v-for="item in dataSource" :key="item.id">
<div class="result-title-wrapper">
<!-- 正在搜索标题根据搜索内容高亮显示 -->
<a-tooltip>
<template #title>
<span v-html="item.title"></span>
</template>
<p class="result-title" v-html="item.title" @click="filePreviewAll(item)"></p>
</a-tooltip>
<div class="result-source-wrapper">
<p class="result-source">{{ resourceTypeOptions.find(i => i.key === item.resourceType).label }}</p>
</div>
<div v-show="judgeAbolish(item)" class="result-source-wrapper" >
<p class="result-abolish">{{ StandardStatus.ABOLISH.text }}</p>
</div>
<p class="result-date">{{ $t('dashboard.generalSearch.releaseDate') }} {{ item.releaseDate || global.emptyLine }}</p>
</div>
<template v-if="retrievalModeCurrent === '1'">
<!-- 全文检索 -->
<div v-html="item.content" class="result-content-wrapper"></div>
</template>
<!--<template v-else>-->
<!-- <div v-if="item.resourceType !== '4'" class="result-content-wrapper">-->
<!-- {{ item.content }}-->
<!-- </div>-->
<!-- <div v-else v-html="item.content" class="result-content-wrapper"></div>-->
<!--</template>-->
</div>
</div>
<a-pagination
v-if="dataSource && dataSource.length > 0"
class="page-content"
show-quick-jumper
show-size-changer
v-model="ipagination.current"
:pageSize="ipagination.pageSize"
:pageSizeOptions="ipagination.pageSizeOptions"
:total="ipagination.total"
:showTotal="ipagination.showTotal"
@change="pageChange"
@showSizeChange="pageSizeChange"
/>
</template>
<a-empty class="empty-content" v-else />
<!-- 检索历史弹框 -->
<search-history-modal class="search-history-modal" ref="searchHistoryModal" @historySearch="historySearch"></search-history-modal>
</a-card>
</a-spin>
</template>
<script>
import SearchHistoryModal from './modules/SearchHistoryModal'
import {
getSearchParamNum,
getSearchParamNumFullText,
generalSearchTitleSearch,
generalSearchFullTextSearch
} from '@/api/dashboard'
import { isHasPermission } from '@/utils/hasPermission'
import { mapGetters } from 'vuex'
import { StandardStatus, EnterpriseStandardStatus } from '@/enums/commonEnums'
import {
getDomesticStandardFormModal,
getDomesticStandardDetail,
getOverseasStandardForm,
getOverseasStandardDocumentInfo
} from '../../../api/standardRegulationLibraryApi'
import {
getEnterpriseStandardAddForm,
getEnterpriseStandardInfoById,
isCanLookDetail
} from '../../../api/enterpriseStandardLibraryApi'
import Vue from 'vue'
import { previewPdf } from '../../../utils/previewPdf'
import { kkFilePreview } from '../../../utils/kkFilePreview'
import { ACCESS_TOKEN } from '../../../store/mutation-types'
import { getFileInfo } from '../../../api/api'
export default {
name: 'GeneralSearch',
components: { SearchHistoryModal },
computed: {
...mapGetters(['defaultHeight']),
cardHeight () {
return `calc(100vh - ${this.defaultHeight['user-info-height']} - ${this.defaultHeight['breadcrumb-height']}`
}
},
data () {
return {
selectOptionLoading: false, // 其他筛选条件部分的loading
loading: false,
searchValue: '', // 搜索框内容
toggleSearchStatus: false, // 检索条件收缩还是展开, false收缩状态
// 检索方式可选择项
retrievalModeOptions: [
{ key: '1', label: '全文检索' },
{ key: '2', label: '标题检索' }
],
retrievalModeCurrent: '1', // 检索方式当前选中项
// 资源类型可选择项
resourceTypeOptions: [
{ key: 'all', label: '全部' },
{ key: '1', label: '国内标准法规' },
{ key: '2', label: '海外标准法规' },
{ key: '3', label: '企业标准' },
{ key: '4', label: '动态消息' }
],
resourceTypeCurrent: 'all', // 资源类型当前选中项
// 标准状态设置可选择项
standardStatusSetOptions: [
{ key: '1', label: '不包含废止标准' },
{ key: '2', label: '包含废止标准' }
],
standardStatusSetCurrent: '1', // 标准状态设置当前选中项
selectOptions: {}, // 其他筛选条件
selectOptionsKeys: [
'standardClass', // 标准类别
'dynamicType', // 动力类型
'applicableVehicle', // 适用车辆类型
'standardProperty', // 标准类型
'standardState', // 状态
'standardCode', // 企业标准代号
'enterpriseStandardState', // 标准状态
'gradeClassification' // 标准等级分类
], // 其他筛选条件中所有的key,为了能按顺序遍历
dataSource: [],
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
total: 0,
showQuickJumper: true,
showSizeChanger: true
},
StandardStatus: StandardStatus,
getFormFunc: getDomesticStandardFormModal,
getDocumentInfoFunc: getDomesticStandardDetail
}
},
mounted () {
// 初始化检索方式
if (this.$route.query.type === 'title') {
// 标题检索
this.retrievalModeCurrent = '2'
} else if (this.$route.query.type === 'fullText') {
// 全文检索
this.retrievalModeCurrent = '1'
}
// 初始化资源类型
if (this.$route.query.resourceType) {
this.resourceTypeCurrent = this.$route.query.resourceType
}
// 初始化搜索内容
if (this.$route.query.searchValue) {
this.searchValue = this.$route.query.searchValue
}
// 资源类型不是全部,并且有搜索内容时获取筛选条件
if (this.resourceTypeCurrent !== 'all' && this.searchValue) {
this.initFilterData()
}
// 有搜索内容时,调用查询方法
if (this.searchValue) {
this.onSearch(1)
}
},
methods: {
isHasPermission,
// 判断是否是废止标准
judgeAbolish (item) {
// console.log('judgeAbolish')
// console.log('item', item)
if (item.resourceType === '3') { // 企业标准 判断 废止
return item.standardState === EnterpriseStandardStatus.ABOLISH.value
} else { // 国内标准 海外标准 判断 废止
return item.standardState === StandardStatus.ABOLISH.value
}
},
// 获取要显示数量的筛选条件的数据
initFilterData (callback) {
const param = {}
// 资源类型
param.resourceType = this.resourceTypeCurrent === 'all' ? null : this.resourceTypeCurrent
// 检索方式
param.searchMode = this.retrievalModeCurrent
// 检索内容
param.searchContent = this.searchValue
// 标准状态设置
param.standardStateSet = this.standardStatusSetCurrent
let func
if (this.retrievalModeCurrent === '1') {
func = getSearchParamNumFullText
} else {
func = getSearchParamNum
}
this.selectOptionLoading = true
func(param).then(res => {
if (res.success) {
// 设置对象key为搜索时需要传给后端的字段名,以及设置选项和字段label
for (const item in res.result) {
if (res.result[item]) {
let label = ''
if (item === 'applicableVehicle') {
// 适用车辆类型
label = this.$t('dashboard.generalSearch.applicableVehicleType')
} else if (item === 'dynamicType') {
// 动力类型
label = this.$t('dashboard.generalSearch.dynamicType')
} else if (item === 'enterpriseStandardState') {
// 标准状态
label = this.$t('dashboard.generalSearch.standardState')
} else if (item === 'gradeClassification') {
// 标准等级分类
label = this.$t('dashboard.generalSearch.standardGradeClassification')
} else if (item === 'standardClass') {
// 标准类别
label = this.$t('dashboard.generalSearch.standardClass')
} else if (item === 'standardCode') {
// 企业标准代号
label = this.$t('dashboard.generalSearch.enterpriseStandardCode')
} else if (item === 'standardProperty') {
// 标准类型
label = this.$t('dashboard.generalSearch.standardType')
} else if (item === 'standardState') {
// 状态
label = this.$t('dashboard.generalSearch.state')
}
res.result[item].unshift({ itemText: this.$t('dashboard.generalSearch.all'), itemValue: 'all' })
this.selectOptions[item] = {
options: res.result[item],
label: label,
current: 'all'
}
} else {
// 该字段为null,删除原本有的该字段
if (Object.keys(this.selectOptions).includes(item)) {
delete this.selectOptions[item]
}
}
}
console.log(this.selectOptions)
callback && callback()
} else {
this.$message.warning(res.message)
this.selectOptions = {}
}
}).finally(() => {
this.selectOptionLoading = false
})
},
// 查询时获取参数
getSearchParam () {
const param = {}
// 检索方式
param.searchMode = this.retrievalModeCurrent
// 资源类型
param.resourceType = this.resourceTypeCurrent === 'all' ? null : this.resourceTypeCurrent
// 检索内容
param.searchContent = this.searchValue
// 标准状态设置
param.standardStateSet = this.standardStatusSetCurrent
// 其他筛选条件
for (const item in this.selectOptions) {
param[item] = this.selectOptions[item].current === 'all' ? null : this.selectOptions[item].current
}
// 分页
param.pageNo = this.ipagination.current
param.pageSize = this.ipagination.pageSize
return param
},
// 查询
onSearch (arg) {
if (arg === 1) {
this.ipagination.current = 1
}
const param = this.getSearchParam()
let func
if (this.retrievalModeCurrent === '1') {
// 是全文检索
func = generalSearchFullTextSearch
} else {
// 是标题检索
func = generalSearchTitleSearch
}
this.loading = true
func(param).then(res => {
if (res.success) {
this.dataSource = res.result.records
if (res.result.total) {
this.ipagination.total = res.result.total
} else {
this.ipagination.total = 0
}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 分页改变
pageChange (page) {
this.ipagination.current = page
this.onSearch()
},
pageSizeChange (current, size) {
console.log(current, size)
this.ipagination.current = current
this.ipagination.pageSize = size
this.onSearch()
},
onInputSearch () {
this.onSearch(1)
if (this.resourceTypeCurrent !== 'all') {
this.initFilterData()
}
},
// 检索方式改变
retrievalModeChange (key) {
this.retrievalModeCurrent = key
this.ipagination = {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
total: 0,
showQuickJumper: true,
showSizeChanger: true
}
// 更新其他筛选项的数据
if (this.resourceTypeCurrent !== 'all') {
this.initFilterData()
}
this.onSearch()
},
// 资源类型改变
resourceTypeChange (key) {
this.resourceTypeCurrent = key
this.ipagination = {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
total: 0,
showQuickJumper: true,
showSizeChanger: true
}
// 更新其他筛选项的数据
if (key !== 'all') {
this.initFilterData()
} else {
this.selectOptions = {}
}
this.onSearch()
},
// 标准状态设置改变
standardStatusSetChange (key) {
this.standardStatusSetCurrent = key
this.ipagination = {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
total: 0,
showQuickJumper: true,
showSizeChanger: true
}
if (this.resourceTypeCurrent !== 'all') {
this.initFilterData()
}
this.onSearch()
},
// 其他检索条件改变
selectOptionsChange (changeKey, value) {
console.log(changeKey, value)
this.selectOptions[changeKey].current = value
this.onSearch(1)
this.$forceUpdate()
},
// 收齐展开点击
handleToggleChange () {
this.toggleSearchStatus = !this.toggleSearchStatus
},
// 点击高级搜索按钮
clickAdvancedSearch () {
this.$router.push({
path: '/advancedSearch'
})
},
// 点击首页按钮
clickHomePage () {
this.$router.push({
path: '/dashboard/analysis'
})
},
// 点击检索历史
clickSearchHistory () {
this.$refs.searchHistoryModal.open()
},
// 跳转详情
goDetail (record) {
let path = ''
// 需要先判断是哪种标准
if (record.resourceType === '1') {
path = '/standardRegulationLibrary/DomesticStandardDetail'
} else if (record.resourceType === '2') {
path = '/standardRegulationLibrary/OverseasStandardDetail'
} else if (record.resourceType === '3') {
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
} else if (record.resourceType === '4') {
path = '/standardRegulationLibrary/DynamicMessageDetail'
}
this.$openPageNewSheet({
path: path,
query: {
id: record.id
}
})
},
// 根据类型,选择调用的函数
adjustFunc (type) {
switch (type) {
case '1':
this.getFormFunc = getDomesticStandardFormModal
this.getDocumentInfoFunc = getDomesticStandardDetail
break
case '2':
this.getFormFunc = getOverseasStandardForm
this.getDocumentInfoFunc = getOverseasStandardDocumentInfo
break
case '3':
this.getFormFunc = getEnterpriseStandardAddForm
this.getDocumentInfoFunc = getEnterpriseStandardInfoById
break
}
},
filePreviewAll (record) {
if (record.resourceType === '4') {
this.goDetail(record)
} else {
this.adjustFunc(record.resourceType)
if (record.resourceType === '3') { // 企标打开需要权限验证
console.log('record', record)
isCanLookDetail(record).then(res => {
console.log('res', res)
if (res.success) {
if (res.result) {
this.filePreview13(record)
} else {
this.$message.warn('您没有查看该文件的权限')
}
} else {
this.$message.warn(res.message)
}
})
} else {
this.filePreview13(record)
}
}
},
/**
* 按顺序打开最新的pdf或word
*/
async filePreview13 (record) {
console.log('filePreview', record)
// 获取并处理过程稿件数据
let res = await this.getFormFunc({ type: 1 })
if (!res.success) {
this.$message.warn(res.message)
console.log('getFormFunc error:', res)
return
}
console.log('res.result', res.result)
const data = res.result || {}
const form = data
const partList = Object.keys(data)
let part
const partZh = '过程稿件'
const partEn = 'Process Manuscript'
if (partList.includes(partEn))part = partEn
if (partList.includes(partZh))part = partZh
console.log('part', part)
const processManuscript = form[part]
console.log('过程稿件', processManuscript)
// 获取并处理详情数据
res = await this.getDocumentInfoFunc({ id: record.id, type: 1 })
if (!res.success) {
this.$message.warn(res.message)
console.log('getDocumentInfoFunc error:', res)
return
}
const detailData = Object.assign({}, res.result || {})
console.log('detailData', detailData)
// 选择合适的文件
let file = null
const pdfFileList = []
const docFileList = []
let newestFileList = []
let manuscriptName = '' // 记录找到哪个稿件就找到文件了
// 按照过程稿件顺序寻找文件
for (const formItem of processManuscript) {
const keyId = formItem.dbFieldName
const keyName = formItem.dbFieldName + '_dictText'
if (!detailData[keyId]) continue
const idArray = detailData[keyId].split(',')
const nameArray = detailData[keyName].split(',')
// 每个过程中可能有多个文件
for (let i = 0; i < idArray.length; i++) {
const id = idArray[i]
const name = nameArray[i]
var suffix = name.substring(name.lastIndexOf('.') + 1).toLowerCase()
if (suffix === 'pdf') {
pdfFileList.push({ id, name, suffix })
}
if (suffix === 'docx') {
docFileList.push({ id, name, suffix })
}
}
// 一旦一个过程中找到pdf或者doc,就可以退出
if (pdfFileList.length > 0 || docFileList.length > 0) {
manuscriptName = formItem.dbFieldName
break
}
}
if (manuscriptName === 'publish_of_original' && record.resourceType === '3') {
// 是企标,在发布稿就找到了,合并pdf和doc文件列表排序
newestFileList = [...pdfFileList, ...docFileList]
} else {
newestFileList = pdfFileList // 默认从pdf里选最新的文件
if (pdfFileList.length === 0)newestFileList = docFileList // 如果没有pdf文件,就从doc文件里选
}
file = await this.selectNewestFile(newestFileList)
if (file === null) { // 没有找到文件就退出
console.log('没有找到符合要求的文件!')
this.$message.error('该标准暂无预览文件,请联系标准管理员。')
return
}
console.log('seletedFile', file)
// 是onlyOffice生成的,预览关联的pdf文件
if (file.pdfId) {
// 说明是onlyOffice生成的
const url = previewPdf(file.pdfId)
window.open(url)
return
}
// 预览选择的文件
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.name}`
console.log(fileFullUrl)
if (file.suffix === 'pdf') { // pdf预览
const url = previewPdf(file.id)
window.open(url)
return
}
kkFilePreview(fileFullUrl)// 其余可预览文件仍使用KKFile进行预览
},
/**
* 从文件列表里选择最新文件
* @param fileList
*/
async selectNewestFile (fileList) {
if (!fileList || fileList.length === 0) return null
if (fileList.length === 1) {
const res = await getFileInfo(fileList[0])
return { ...fileList[0], ...res.result }
}
const newFileList = []
// 获取时间信息
for (const file of fileList) {
const res = await getFileInfo(file)
newFileList.push({ ...file, ...res.result })
}
// 排序取出最新
newFileList.sort((a, b) => b.id - a.id) // 日期只精确到天,用id更能区分
console.log('newFileList', newFileList)
return newFileList[0]
},
// 检索弹框点击历史查数据
historySearch (searchObj) {
console.log('--------historySearch--------', searchObj)
// 清空其他筛选记录
this.selectOptions = {}
// 设置历史记录中的值
this.retrievalModeCurrent = searchObj.searchMode // 检索方式
this.resourceTypeCurrent = searchObj.resourceType || 'all' // 资源类型
this.searchValue = searchObj.searchValue // 搜索内容
if (this.resourceTypeCurrent !== 'all' && this.searchValue) {
this.initFilterData(() => {
const query = {
applicableVehicle: searchObj.applicableVehicle, // 适用车辆类型
dynamicType: searchObj.dynamicType, // 动力类型
enterpriseStandardState: searchObj.enterpriseStandardState, // 标准状态
gradeClassification: searchObj.gradeClassification, // 标准等级分类
standardClass: searchObj.standardClass, // 标准类别
standardCode: searchObj.standardCode, // 企业标准代号
standardProperty: searchObj.standardProperty, // 标准类型
standardState: searchObj.standardState // 状态
}
const queryObj = {}
for (const item in query) {
if (query[item]) {
queryObj[item] = query[item]
}
}
for (const item in queryObj) {
this.selectOptions[item].current = queryObj[item]
}
this.onSearch(1)
})
} else {
this.onSearch(1)
}
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
/deep/ .ant-card-body {
padding: 0;
//height: calc(100vh - 45px - 59px - 12px);
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
}
// 搜索没有按钮
.search-no-btn {
/deep/ .ant-input {
border-right: 1px solid #d9d9d9;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
}
// 头部搜索区域
.top-search-div {
width: 100%;
min-height: 98px;
background-image: url("../../../assets/image/searchPageBack.png");
background-size: 100% 100%;
background-repeat: no-repeat;
display: flex;
justify-content: center;
align-items: center;
.input-search {
width: 468px;
height: 44px;
/deep/ .ant-input {
height: 42px;
line-height: 42px;
}
/deep/ .ant-input-search-button {
height: 42px;
}
}
.search-link {
height: 44px;
line-height: 44px;
margin-left: 32px;
border-bottom: 1px solid @primary-color;
}
}
// 筛选区域
.filter-area-div {
padding: 10px 20px;
//box-sizing: border-box;
max-height: 300px;
overflow-y: auto;
.filter-row {
height: auto;
line-height: 38px;
display: flex;
}
.label-col {
width: 200px;
text-align: left;
color: #86909c;
}
.option-col {
width: calc(100% - 200px);
height: auto;
color: #1D2129;
display: flex;
align-items: center;
flex-wrap: wrap;
.option-div {
width: auto;
height: 26px;
line-height: 26px;
padding: 0 12px;
cursor: pointer;
margin: 6px 4px;
overflow: hidden;
white-space: nowrap;
}
.option-span-num {
color: @primary-color;
}
}
}
// 选中项样式
.option-curr {
background: rgba(213, 44, 38, 0.08);
color: @primary-color;
border-radius: 4px 4px 4px 4px;
}
// 收缩展开容器
.toggle-div {
text-align: center;
cursor: pointer;
padding: 10px 0;
border-bottom: 1px solid #E5E6EB;
/deep/ .anticon {
height: 8px;
}
}
// 查询结果展示区域
.result-wrapper {
flex: 1;
overflow-y: auto;
padding: 16px 24px 0;
}
// 高亮样式
/deep/ .highlight-Es-class {
color: @primary-color;
word-wrap: break-word;
}
// 单个查询结果
.result-div {
width: 100%;
height: auto;
background: #F8F8F8;
border-radius: 4px 4px 4px 4px;
padding: 20px;
box-sizing: border-box;
margin-bottom: 16px;
.result-title-wrapper {
display: flex;
align-items: center;
p {
margin-bottom: 0;
}
.result-title {
color: #1D2129;
font-size: 18px;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 600;
line-height: 28px;
margin-right: 20px;
max-height: calc(100% - 340px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: pointer;
}
.result-source-wrapper {
min-width: 100px;
}
.result-source {
display: inline-block;
width: auto;
height: 28px;
line-height: 28px;
background: @primary-color;
border-radius: 4px 4px 4px 4px;
font-size: 14px;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
padding: 0 8px;
}
.result-abolish {
margin-left: 10px;
display: inline-block;
width: auto;
height: 28px;
line-height: 28px;
background: #B7B7B7;
border-radius: 4px 4px 4px 4px;
font-size: 14px;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FFFFFF;
padding: 0 8px;
}
.result-date {
min-width: 200px !important;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #86909C;
line-height: 19px;
text-align: right;
white-space: nowrap;
flex: 1;
}
}
.result-content-wrapper {
width: 100%;
max-height: 40px;
font-size: 14px;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #1D2129;
line-height: 20px;
margin-top: 10px;
//display: -webkit-box; /* 将元素作为块级元素进行布局 */
//-webkit-box-orient: vertical; /* 垂直方向对齐 */
//-webkit-line-clamp: 2; /* 最多显示两行文本 */
overflow: hidden; /* 控制溢出内容不被显示 */
text-overflow: ellipsis; /* 当内容溢出时显示省略号 */
.result-field-span {
margin-right: 12px;
}
}
}
.empty-content {
margin-top: 20px;
}
// 分页
.page-content {
height: 32px;
text-align: right;
margin: 10px 24px 24px 0;
}
// 检索历史全屏下的样式
.search-history-modal.ant-modal-root.j-modal-box.fullscreen {
/deep/ .ant-spin-nested-loading {
height: 100%;
.ant-spin-container {
height: 100%;
}
.content-wrapper {
max-height: calc(100% - 20px);
}
}
}
</style>
@@ -0,0 +1,277 @@
<template>
<j-modal
:title="$t('dashboard.generalSearch.searchHistory')"
:maskClosable="false"
:width="width"
:visible="visible"
:confirm-loading="confirmLoading"
switchFullscreen
@cancel="handleCancel">
<a-spin :spinning="confirmLoading">
<template v-if="model && Object.keys(model).length > 0">
<a-checkbox :indeterminate="indeterminate" :checked="checkAll" @change="onCheckAllChange">
{{ $t('checkedAll') }}
</a-checkbox>
<div class="content-wrapper" ref="contentWrapper" @scroll="contentWrapperScroll">
<a-checkbox-group v-model="checkedArr" @change="onCheckChange">
<template v-for="(item, key) in model">
<p :key="key" class="time-p">{{ isCurrentDate(key) ? '最近' : key }}</p>
<div v-for="arrItem in item" :key="arrItem.id" class="search-value-div">
<a-checkbox :value="arrItem.id"></a-checkbox>
<a-tooltip>
<template #title>
{{ arrItem.searchContent }}
</template>
<p class="search-content" @click="clickSearchContent(arrItem)">{{ arrItem.searchContent }}</p>
</a-tooltip>
<i v-has="'dashboard:generalSearch:searchHistory'" class="iconfont icon-shanchu_" @click="handleDelete(arrItem.id)"></i>
</div>
</template>
</a-checkbox-group>
</div>
<!--<div class="record-item" v-for="record in recordList" :key="record.id">-->
<!-- {{ record.content }}-->
<!--</div>-->
</template>
<a-empty v-else />
</a-spin>
<template v-slot:footer>
<a-button v-has="'dashboard:generalSearch:searchHistory'" type="primary" ghost @click="handleBatchDel">{{ $t('batchDelete') }}</a-button>
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import {
getSearchHistoryData,
deleteSearchHistoryById,
batchDeleteSearchHistory
} from '@/api/dashboard'
export default {
name: 'SearchHistoryModal',
data () {
return {
width: 500,
visible: false,
confirmLoading: false,
indeterminate: false,
checkAll: false,
model: {},
page: {
pageNo: 1,
pageSize: 100,
total: 0,
pages: 0
},
dataList: [],
checkedArr: []
}
},
methods: {
open () {
this.page = {
pageNo: 1,
pageSize: 100,
total: 0,
pages: 0
}
this.initData()
this.visible = true
},
// 是否显示最近
isCurrentDate (createTime) {
let createDateStr = createTime.split(' ')[0]
createDateStr = createDateStr.replace(/([年月])/g, '-')
createDateStr = createDateStr.replace(/日/g, '')
const currentDate = new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate()
if (createDateStr === currentDate) {
return true
} else {
return false
}
},
// 内容展示区域滚动事件
contentWrapperScroll () {
const contentWrapper = this.$refs.contentWrapper
// 滚动条触底,并且还不是最后一页时获取下一页
if (contentWrapper.scrollTop + contentWrapper.clientHeight === contentWrapper.scrollHeight && this.page.pageNo < this.page.pages) {
this.page.pageNo++
this.initData()
}
},
// 获取列表数据
initData (arg) {
if (arg === 1) {
this.page.pageNo = 1
}
const param = {}
param.pageNo = this.page.pageNo
param.pageSize = this.page.pageSize
this.confirmLoading = true
getSearchHistoryData(param).then(res => {
if (res.success) {
if (this.page.pageNo === 1) {
this.dataList = res.result.records
} else {
this.dataList = this.dataList.concat(res.result.records)
}
this.page.pages = res.result.pages
this.page.total = res.result.total
this.indeterminate = !!this.checkedArr.length && this.checkedArr.length < this.dataList.length
for (const item of res.result.records) {
const modelKeys = Object.keys(this.model)
if (!modelKeys.includes(item.createTime)) {
// 还没有该日期
this.model[item.createTime] = []
}
this.model[item.createTime].push(item)
}
}
}).finally(() => {
this.confirmLoading = false
})
},
// 点击标题跳转
clickSearchContent (item) {
const query = {
searchMode: item.searchMode, // 检索方式
resourceType: item.resourceType, // 资源类型
searchValue: item.searchContent, // 搜索内容
applicableVehicle: item.applicableVehicle, // 适用车辆类型
dynamicType: item.dynamicType, // 动力类型
enterpriseStandardState: item.enterpriseStandardState, // 标准状态
gradeClassification: item.gradeClassification, // 标准等级分类
standardClass: item.standardClass, // 标准类别
standardCode: item.standardCode, // 企业标准代号
standardProperty: item.standardProperty, // 标准类型
standardState: item.standardState // 状态
}
const queryObj = {}
// 去掉为空的数据
for (const item in query) {
if (query[item]) {
queryObj[item] = query[item]
}
}
this.$emit('historySearch', queryObj)
this.handleCancel()
},
// 多选变化
onCheckChange (checkedValue) {
console.log(checkedValue)
this.checkedArr = checkedValue
this.indeterminate = !!checkedValue.length && checkedValue.length < this.dataList.length
this.checkAll = checkedValue.length === this.dataList.length
},
// 全选变化
onCheckAllChange (e) {
console.log(e)
this.checkedArr = e.target.checked ? this.dataList.map(item => item.id) : []
this.indeterminate = false
this.checkAll = e.target.checked
},
// 删除
handleDelete (id) {
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
deleteSearchHistoryById({ id: id }).then(res => {
if (res.success) {
this.$message.success(res.message)
// 判断当前删除的数据是否是最后一页的最后一条数据,如果是的话页码减一
// if (this.page.pageNo > 1 && ((this.page.pageNo - 1) * this.page.pageSize) + 1 === this.page.total) {
// this.page.pageNo -= 1
// }
this.model = {}
this.dataList = []
this.initData(1)
this.indeterminate = !!this.checkedArr.length && this.checkedArr.length < this.dataList.length
this.checkAll = this.checkedArr.length > 0 && this.checkedArr.length === this.dataList.length
} else {
this.$message.warning(res.message)
}
})
}
})
},
// 批量删除
handleBatchDel () {
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
batchDeleteSearchHistory({ id: this.checkedArr.join(',') }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.checkedArr = []
this.model = {}
this.dataList = []
this.initData(1)
this.indeterminate = !!this.checkedArr.length && this.checkedArr.length < this.dataList.length
this.checkAll = this.checkedArr.length > 0 && this.checkedArr.length === this.dataList.length
} else {
this.$message.warning(res.message)
}
})
}
})
},
// 取消
handleCancel () {
this.visible = false
this.model = {}
this.dataList = []
this.checkedArr = []
this.indeterminate = false
this.checkAll = false
}
}
}
</script>
<style scoped lang="less">
.content-wrapper {
padding-right: 5px;
max-height: 600px;
overflow-y: auto;
}
.time-p {
margin: 10px 0;
color: @primary-color;
}
/deep/ .ant-checkbox-group {
width: 100%;
}
.search-value-div {
width: 100%;
height: 30px;
display: flex;
justify-content: flex-start;
align-items: center;
border: 1px solid #E5E6EB;
padding: 0 10px;
border-radius: 4px 4px 4px 4px;
margin-bottom: 5px;
p {
margin-bottom: 0;
flex: 1;
margin-left: 10px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.iconfont {
margin-left: auto;
color: @primary-color;
}
}
.search-content {
cursor: pointer;
}
</style>
@@ -0,0 +1,569 @@
<template>
<internal-detail-page :loading="loading" :title="$t('docTool.comparison.comparisonResults')" :content-padding="0">
<template #titleRightCustom>
<!--查看差异项-->
<a-button type="primary"
ghost
class="header-btn"
icon="eye"
@click="viewVarianceAssessmentClick"
v-has="'docComparison:result:viewVarianceAssessment'">
{{ $t('docTool.comparison.viewVarianceAssessment') }}
</a-button>
<!--查看一致项-->
<a-button type="primary"
ghost
class="header-btn"
icon="eye"
@click="viewConsistentAssessmentClick"
v-has="'docComparison:result:viewConsistentAssessment'">
{{ $t('docTool.comparison.viewConsistentAssessment') }}
</a-button>
<!--查看全部-->
<a-button type="primary" ghost class="header-btn" icon="eye" @click="viewAllClick" v-has="'docComparison:result:viewAll'">
{{ $t('docTool.comparison.viewAll') }}
</a-button>
<!--导出对比报告-->
<a-button type="primary"
ghost
class="header-btn"
icon="export"
@click="exportComparisonReportClick"
v-has="'docComparison:result:exportComparisonReport'">
{{ $t('docTool.comparison.exportComparisonReport') }}
</a-button>
<!--评论-->
<a-button type="primary" ghost class="header-btn" icon="message" @click="commentClick" v-has="'docComparison:result:comment'">
{{ $t('docTool.comparison.comment') }}
</a-button>
</template>
<div class="result-content">
<div class="result-content-header" :class="{'result-content-header-has-scroll': hasScrollBar}">
<a-checkbox @change="onChange" :indeterminate="indeterminate" :checked="checkAll"></a-checkbox>
<div class="result-content-header-content">
<div class="result-content-header-content-item result-content-header-content-item-standard"
:title="resultData.serialNumberLeft + resultData.fileNameLeft"
:class="{'result-content-header-content-item-no-operation': isDisplay}">
{{ $t('docTool.comparison.selectedStandard') }}{{ resultData.serialNumberLeft }}
<!--点击文件名可以预览-->
<span style="cursor: pointer"
@click="fileClick(resultData.fileNameLeft,resultData.fileIdLeft)">{{ resultData.fileNameLeft }}</span>
</div>
<div class="result-content-header-content-item result-content-header-content-item-standard"
:title="resultData.serialNumberRight + resultData.fileNameRight"
:class="{'result-content-header-content-item-no-operation':isDisplay}">
{{ $t('docTool.comparison.selectedStandard') }}{{ resultData.serialNumberRight }}
<span style="cursor: pointer"
@click="fileClick(resultData.fileNameRight,resultData.fileIdRight)">{{ resultData.fileNameRight }}</span>
</div>
<div class="result-content-header-content-item result-content-header-content-item-comment"
:title="$t('docTool.comparison.comparisonDifferenceComment')">
{{ $t('docTool.comparison.comparisonDifferenceComment') }}
</div>
<div class="result-content-header-content-item result-content-header-content-item-operation" v-if="!isDisplay">
{{ $t('operation') }}
</div>
</div>
</div>
<!--条款对比内容-->
<div class="scroll-content">
<a-checkbox-group @change="checkboxChange" v-model="checkboxList">
<div class="clause-item" v-for="(item,index) in resultData.resList" :key="index">
<a-checkbox :value="item.id" class="clause-item-checkbox"></a-checkbox>
<div class="clause-item-content" :class="{'clause-item-content-selected':checkboxList.join(',').includes(item.id)}">
<!--内容isDisplay===true时不显示操作列内容部分宽度设为100%-->
<!--第一个标准的内容-->
<div class="clause-item-content-standard" :class="{'clause-item-content-standard-no-operation': isDisplay}">
<div class="clause-item-content-standard-title">
<span>{{ item.itemsNumLeft }}</span>
<span style="margin-left: 10px">{{ item.itemsNameLeft }}</span>
</div>
<div class="clause-item-content-text" v-html="item.itemsTextLeft"></div>
</div>
<!--第二个标准的内容-->
<div class="clause-item-content-standard" :class="{'clause-item-content-standard-no-operation': isDisplay}">
<div class="clause-item-content-standard-title">
<span>{{ item.itemsNumRight }}</span>
<span style="margin-left: 10px">{{ item.itemsNameRight }}</span>
</div>
<div class="clause-item-content-text" v-html="item.itemsTextRight"></div>
</div>
<!--对比差异评论-->
<div class="clause-item-content-comment">
<div class="clause-item-content-text">
{{ item.comment }}
</div>
</div>
<!--操作-->
<div class="clause-item-content-operation" v-if="!isDisplay">
<!--编辑-->
<a class="text-button" @click="comparaEdit(item)" v-has="'docComparison:result:edit'">{{ $t('edit') }}</a>
<!--删除-->
<a class="text-button" @click="comparaDelete(item)" v-has="'docComparison:result:delete'">{{ $t('delete') }}</a>
</div>
</div>
</div>
</a-checkbox-group>
</div>
<!--全文评论部分-->
<div class="clause-item full-text-review">
<a-checkbox @change="fullCheckboxChange" v-model="fullCheckbox" class="clause-item-checkbox"></a-checkbox>
<div class="full-text-review-content" :class="{'clause-item-content-selected':fullCheckbox}">
<div class="full-text-review-text" :class="{'full-text-review-text-no-operation':isDisplay}">
<a-tooltip>
<template #title>{{ $t('docTool.comparison.fullTextComments') }}{{ resultData.comments }}</template>
<span>{{ $t('docTool.comparison.fullTextComments') }}{{ resultData.comments }}</span>
</a-tooltip>
</div>
<div class="full-text-review-operation" v-if="!isDisplay">
<!--全文评论-编辑-->
<a class="text-button" @click="fullTextEdit" v-has="'docComparison:result:editFullTextComments'">{{ $t('edit') }}</a>
<!-- <a class="text-button">{{$t('delete')}}</a>-->
</div>
</div>
</div>
</div>
<!--评论-->
<comment-list ref="commentListRef" />
<!--编辑对比评论-->
<comparison-result-edit-modal ref="comparisonResultEditModal" @ok="getData" />
<div id="images">
<div class="image" v-viewer="{movable: false}">
<img v-show="image" :src="imageUrl">
</div>
</div>
</internal-detail-page>
</template>
<script>
import { getAction, downloadFile, getFileAccessHttpUrl } from '../../../api/manage.js'
import { previewPdf } from '../../../utils/previewPdf.js'
import { Base64 } from 'js-base64'
import { mapGetters } from 'vuex'
import CommentList from './modules/CommentList.vue'
import ComparisonResultEditModal from './modules/ComparisonResultEditModal.vue'
import InternalDetailPage from '../../../components/InternalDetailPage.vue'
import { deleteComparisonResult, getComparisonResultDetail } from '../../../api/documentToolApi.js'
export default {
name: 'ComparisonResults',
components: { CommentList, ComparisonResultEditModal, InternalDetailPage },
data () {
return {
loading: false,
checkboxList: [],
isVarianceOne: false,
isVarianceTwo: false,
isVarianceThe: false,
downLoadFileUrl: window._CONFIG.domianPreviewURL + '/sys/common/download',
downLoadImgUrl: window._CONFIG.domianWebImgURL + '/sys/common/download',
indeterminate: false,
checkAll: false,
isDisplay: false,
fullCheckbox: false,
resultData: {},
comment: '',
language: '',
url: {
exportData: '/compare/sarFileCompareItemComment/exportResXls'
},
image: false,
imageUrl: '',
hasScrollBar: false // 页面中间部分是否有滚动条
}
},
mounted () {
if (this.$route.query.isDisplay === '2') {
this.isDisplay = false
} else {
this.isDisplay = true
}
this.language = localStorage.getItem('language') || ''
this.getData()
},
methods: {
...mapGetters(['userInfo']),
goBack () {
this.$router.push({
path: '/docTool/docComparison'
})
},
// 评论按钮点击
commentClick () {
this.$refs.commentListRef.getData()
},
// 导出对比报告按钮点击
exportComparisonReportClick () {
const checkboxList = JSON.parse(JSON.stringify(this.checkboxList))
const fullCheckbox = this.fullCheckbox
const query = {
selectIds: checkboxList.join(','),
includeComments: fullCheckbox,
infoId: this.$route.query.id,
comment: this.comment
}
downloadFile(this.url.exportData, this.resultData.serialNumberLeft + ' - ' + this.resultData.serialNumberRight + this.$t('docTool.comparison.comparisonResults') + '.xls', query, this.deselect)
},
deselect () {
this.checkboxList = []
this.fullCheckbox = false
this.indeterminate = false
this.checkAll = false
},
// 查看一致性按钮点击
viewConsistentAssessmentClick () {
this.comment = '一致'
this.isVarianceTwo = true
this.isVarianceOne = false
this.isVarianceThe = false
this.getData()
},
// 查看差异项按钮点击
viewVarianceAssessmentClick () {
this.isVarianceOne = true
this.isVarianceTwo = false
this.isVarianceThe = false
this.comment = '差异'
this.getData()
},
// 查看全部按钮点击
viewAllClick () {
this.comment = ''
this.isVarianceThe = true
this.isVarianceOne = false
this.isVarianceTwo = false
this.getData()
},
// 点击文件名
fileClick (name, id) {
const fileName = name
const index1 = fileName.lastIndexOf('.')
const index2 = fileName.length
const fileSuffix = fileName.substring(index1, index2)
if (fileSuffix === '.pdf') {
// window.open('/pdf/web/viewer.html?file=' + encodeURIComponent('/jero-boot/sys/common/pdf/viewFile?id=' + id + '&userName=' + this.userInfo().username))
const url = previewPdf(id)
window.open(url)
} else if (fileSuffix === '.docx' || fileSuffix === '.doc') {
const url = window._CONFIG.onlinePreviewDomainURL + '?url=' + Base64.encode(this.downLoadFileUrl + '/' + id + fileSuffix)
window.open(url, '_blank')
} else if (fileSuffix === '.xlsx' || fileSuffix === '.xls') {
const url = window._CONFIG.onlinePreviewDomainURL + '?url=' + Base64.encode(this.downLoadFileUrl + '/' + id + fileSuffix)
window.open(url, '_blank')
} else if (fileSuffix === '.png' || fileSuffix === '.jpeg' || fileSuffix === '.gif' || fileSuffix === '.jpg') {
// const url = window._CONFIG.onlinePreviewDomainURL + '?url=' + Base64.encode(this.downLoadImgUrl + '/' + id + fileSuffix)
// window.open(url, '_blank')
this.image = true
this.imageUrl = getFileAccessHttpUrl(id)
// 获取viewer实例
const viewer = this.$el.querySelector('.image').$viewer
// 调用show方法进行显示预览图
viewer.show()
} else {
downloadFile('/sys/common/downLoadFile', name, { id: id })
}
},
// 获取对比结果详情
getData () {
const query = {
id: this.$route.query.id,
infoId: this.$route.query.id,
comment: this.comment
}
this.loading = true
getComparisonResultDetail(query).then((res) => {
if (res.success) {
this.resultData = res.result || {}
// 处理页面中部滚动导致表格对不齐的问题
this.$nextTick(() => {
const scrollBoxDom = document.getElementsByClassName('scroll-content')[0]
if (scrollBoxDom && scrollBoxDom.scrollHeight > scrollBoxDom.clientHeight) {
this.hasScrollBar = true
} else {
this.hasScrollBar = false
}
})
} else {
this.resultData = {}
}
}).finally(() => {
this.loading = false
})
},
// 复选框组改变
checkboxChange (checkedList) {
this.indeterminate = (!!checkedList.length && checkedList.length < this.resultData.resList.length) || (this.fullCheckbox && this.checkboxList.length < this.resultData.resList.length) || (!this.fullCheckbox && !!checkedList.length)
this.checkAll = checkedList.length === this.resultData.resList.length && this.fullCheckbox
},
// 全文评论复选框改变
fullCheckboxChange (event) {
this.indeterminate = (!!this.checkboxList.length && this.checkboxList.length < this.resultData.resList.length) || (event.target.checked && this.checkboxList.length < this.resultData.resList.length) || (!this.fullCheckbox && !!this.checkboxList.length)
this.checkAll = this.checkboxList.length === this.resultData.resList.length && event.target.checked
},
// 全选复选框改变
onChange (event) {
this.checkAll = event.target.checked
if (event.target.checked) {
this.resultData.resList.forEach(res => {
this.checkboxList.push(res.id)
})
this.fullCheckbox = true
this.indeterminate = false
} else {
this.checkboxList = []
this.fullCheckbox = false
}
if (this.checkboxList.length === 0 && !this.fullCheckbox) {
this.checkAll = false
this.indeterminate = false
}
},
// 全文评论编辑点击
fullTextEdit (item) {
this.$refs.comparisonResultEditModal.title = this.$t('docTool.comparison.fullTextComments')
this.$refs.comparisonResultEditModal.open({ comment: this.resultData.comments || '' })
},
// 对比结果编辑按钮点击
comparaEdit (item) {
this.$refs.comparisonResultEditModal.title = this.$t('docTool.comparison.comparativeComments')
this.$refs.comparisonResultEditModal.open(item)
},
// 对比结果删除按钮点击
comparaDelete (val) {
this.$confirm({
content: this.$t('confirmDeletion'),
onOk: () => {
deleteComparisonResult({ id: val.id }).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.getData()
} else {
this.$message.warning(res.message)
}
})
}
})
}
}
}
</script>
<style scoped lang="less">
.result-content {
padding: 24px 32px 0 32px;
box-sizing: border-box;
background: #fff;
height: 100%;
&-header {
width: 100%;
height: 56px;
line-height: 56px;
margin-bottom: 20px;
display: flex;
align-items: center;
}
&-header > .ant-checkbox-wrapper {
margin-left: 20px;
}
}
.result-content-header-content {
width: calc(100% - 56px);
margin-left: 20px;
display: inline-block;
height: 56px;
font-size: 14px;
font-family: Blue Sky Noto, sans-serif;
font-weight: bold;
color: #040B29;
background: rgba(4, 11, 41, 0.0300);
border-radius: 4px;
&-item {
height: 56px;
display: inline-block;
border-right: 1px #EFF1F3 solid;
padding: 0 24px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
&-item:last-child {
border-right: none;
}
&-item-standard {
width: calc((100% - 128px - 260px) / 2);
span {
cursor: pointer;
}
}
&-item-no-operation {
width: calc((100% - 260px) / 2);
}
&-item-comment {
width: 260px;
}
&-item-operation {
width: 128px;
}
}
.result-content-header-has-scroll {
padding-right: 8px;
}
.scroll-content {
height: calc(100% - 76px - 71px - 58px);
overflow: auto;
}
.ant-checkbox-group {
width: 100%;
}
.clause-item {
width: 100%;
height: auto;
display: flex;
align-items: center;
&-checkbox {
margin-left: 20px;
}
}
.clause-item-content {
width: calc(100% - 56px);
margin-left: 20px;
border: 1px solid #EFF1F3;
border-radius: 4px;
overflow: hidden;
display: flex;
align-items: stretch;
&-selected {
border: 1px solid #00B3BE;
background: rgba(0, 179, 190, 0.0300);
}
&-standard {
width: calc((100% - 128px - 260px) / 2);
padding: 24px;
border-right: 1px #EFF1F3 solid;
&-title {
font-size: 16px;
font-family: Blue Sky Noto, sans-serif;
font-weight: 400;
color: #040B29;
}
}
&-standard-no-operation {
width: calc((100% - 260px) / 2);
}
&-text {
font-size: 14px;
font-weight: 400;
color: #040B29;
margin-top: 10px;
/deep/ table {
width: 100%;
}
}
&-comment {
width: 260px;
padding: 24px;
border-right: 1px #EFF1F3 solid;
}
&-operation {
width: 128px;
display: flex;
align-items: center;
justify-content: center;
.text-button {
margin-left: 10px;
}
.text-button:first-child {
margin-left: 0;
}
}
}
.full-text-review {
height: 109px;
margin: 10px 0;
&-content {
margin-left: 20px;
height: 100%;
flex: 1;
width: 0;
display: flex;
align-items: stretch;
border: 1px #EFF1F3 solid;
border-radius: 4px;
}
&-text {
width: calc(100% - 128px);
padding: 24px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
text-overflow: ellipsis;
/*! autoprefixer: off */
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
/*! autoprefixer: on;*/
word-break: break-word;
span {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
text-overflow: ellipsis;
/*! autoprefixer: off */
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
/*! autoprefixer: on;*/
word-break: break-word;
}
}
&-text-no-operation {
width: 100%;
}
&-operation {
width: 128px;
display: flex;
justify-content: center;
align-items: center;
}
}
.header-btn {
margin-left: 12px;
}
.header-btn:first-child {
margin-left: 0;
}
</style>
@@ -0,0 +1,396 @@
<template>
<a-card :bordered="false">
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<!--文本号-->
<a-col :md="6" :sm="8">
<a-form-item :label="$t('docTool.comparison.textNumber')" :labelCol="{span: 6}" :wrapperCol="{span: 14}">
<a-input :placeholder="$t('pleaseEnter') + $t('docTool.comparison.textNumber')"
v-model="queryParam.serialNumber"></a-input>
</a-form-item>
</a-col>
<!--文本名称-->
<a-col :md="6" :sm="8">
<a-form-item :label="$t('docTool.comparison.textName')" :labelCol="{span: 6}" :wrapperCol="{span: 14}">
<a-input :placeholder="$t('pleaseEnter') + $t('docTool.comparison.textName')"
v-model="queryParam.title"></a-input>
</a-form-item>
</a-col>
<!--发布情况-->
<a-col :md="6" :sm="8">
<a-form-item :label="$t('docTool.comparison.releaseSituation')" :labelCol="{span: 6}" :wrapperCol="{span: 14}">
<j-dict-select-tag v-model="queryParam.releaseState"
:placeholder="$t('pleaseSelect')+$t('docTool.comparison.releaseSituation')"
type="select"
:triggerChange="false" dictCode="release_state" />
</a-form-item>
</a-col>
<div style="float: right;overflow: hidden;margin-right: 11px" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search" v-has="'documentComparison:search'">{{
$t('query')
}}
</a-button>
<a-button style="margin-left: 8px" icon="reload" @click="searchReset">{{ $t('reset') }}</a-button>
</div>
</a-row>
</a-form>
</div>
<div class="table-operator">
<!--发起对比-->
<a-button icon="block" type="primary" @click="initiatingProcessClick" v-has="'documentComparison:initiateComparison'">
{{ $t('docTool.comparison.initiateComparison') }}
</a-button>
<!--批量删除-->
<a-button type="primary" icon="delete" ghost @click="batchDelete" v-has="'documentComparison:deleteBatch'">
{{ $t('batchDelete') }}
</a-button>
</div>
<div>
<j-table
ref="table"
size="middle"
:columns="columns"
:data-source="dataSource"
rowKey="id"
:can-drag="true"
:loading="loading"
:pagination="ipagination"
:scroll="{x: '100%', y: yScrollHeight}"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:operation-list="operationList"
@change="handleTableChange"
@operationClick="operationClick">
<!--旧文本跳转标准详情-->
<template v-slot:oldTextDetail="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text can-click-table-text" v-if="text || text === 0" @click="toDetailPage('old', record)">{{
text
}}
</div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
<!--新文本跳转标准详情-->
<template v-slot:newTextDetail="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text can-click-table-text" v-if="text || text === 0" @click="toDetailPage('new', record)">{{
text
}}
</div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</j-table>
</div>
</a-card>
</template>
<script>
import '../../../assets/less/common.less'
import { postAction } from '../../../api/manage.js'
import { mapGetters } from 'vuex'
import JDictSelectTag from '../../../components/dict/JDictSelectTag.vue'
import JTable from '../../../components/jero/JTable.vue'
import { JeroListMixin } from '../../../mixins/JeroListMixin.js'
import { StandardSource } from '../../../enums/commonEnums.js'
import { withdrawPublication } from '../../../api/documentToolApi.js'
export default {
name: 'DocumentComparison',
components: { JDictSelectTag, JTable },
mixins: [JeroListMixin],
data () {
return {
// url传参严格按照当前命名
url: {
list: '/compare/sarFileCompareInfo/page',
delete: '/compare/sarFileCompareInfo/delete',
deleteBatch: '/compare/sarFileCompareInfo/deleteBatch'
},
loading: false,
downLoadFileUrl: window._CONFIG.domianPreviewURL + '/sys/common/download',
downLoadImgUrl: window._CONFIG.domianWebImgURL + '/sys/common/download',
userInfoQuery: {},
columns: [
// 旧文档文本号
{
title: this.$t('docTool.comparison.oldDocumentTextNumber'),
dataIndex: 'serialNumberLeft',
width: 170,
ellipsis: true,
scopedSlots: { customRender: 'oldTextDetail' }
},
// 旧文档文本名称
{
title: this.$t('docTool.comparison.oldDocumentTextName'),
dataIndex: 'titleLeft',
width: 170,
scopedSlots: { customRender: 'oldTextDetail' }
},
// 旧文本状态
{
title: this.$t('docTool.comparison.oldTextState'),
width: 150,
dataIndex: 'fileTypeLeft_dictText'
},
// 新文档文本号
{
title: this.$t('docTool.comparison.newDocumentTextNumber'),
dataIndex: 'serialNumberRight',
width: 170,
scopedSlots: { customRender: 'newTextDetail' }
},
// 新文档文本名称
{
title: this.$t('docTool.comparison.newDocumentTextName'),
dataIndex: 'titleRight',
width: 170,
scopedSlots: { customRender: 'newTextDetail' }
},
// 新文本状态
{
title: this.$t('docTool.comparison.newTextState'),
width: 150,
dataIndex: 'fileTypeRight_dictText'
},
// 发布情况
{
title: this.$t('docTool.comparison.releaseSituation'),
width: 150,
dataIndex: 'releaseState_dictText'
},
// 创建人
{
title: this.$t('creator'),
width: 180,
dataIndex: 'createNameNo'
},
// 备注
{
title: this.$t('remarks'),
width: 270,
dataIndex: 'remark'
},
// 操作
{
title: this.$t('operation'),
fixed: 'right',
width: 250,
scopedSlots: { customRender: 'action' }
}
],
operationList: [
// 对比结果
{
text: this.$t('docTool.comparison.comparisonResults'),
clickEvent: 'comparisonResultsClick',
has: 'documentComparison:comparisonResults'
},
// 发布
{
text: this.$t('release'),
clickEvent: 'subscribe',
has: 'documentComparison:publishWithdraw',
show: (record) => {
return record.createBy === this.userInfo.username && (!record.releaseState || record.releaseState === 'draft')
}
},
// 撤回
{
text: this.$t('withdraw'),
clickEvent: 'subscribe',
has: 'documentComparison:publishWithdraw',
show: (record) => {
return record.createBy === this.userInfo.username && !(!record.releaseState || record.releaseState === 'draft')
}
},
// 编辑
{
text: this.$t('edit'),
clickEvent: 'edit',
has: 'documentComparison:edit',
show: (record) => {
return (record.createBy === this.userInfo.username || this.administrators) && (!record.releaseState || record.releaseState === 'draft')
}
},
// 删除
{
text: this.$t('delete'),
clickEvent: 'handleDelete',
has: 'documentComparison:delete',
show: (record) => {
return (record.createBy === this.userInfo.username || this.administrators) && (!record.releaseState || record.releaseState === 'draft')
}
}
]
}
},
computed: {
...mapGetters(['userInfo']),
administrators () {
const adminRoleList = window._CONFIG.adminRoleCode.split(',')
const userRoleList = this.userInfo.roleCode.split(',')
return userRoleList.some(tt => adminRoleList.includes(tt))
}
},
created () {
if (this.$route.query.serialNumber) {
this.queryParam.serialNumber = this.$route.query.serialNumber
this.queryParam = { ...this.queryParam }
}
this.loadData(1)
},
methods: {
batchDelete () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectLeastOne'))
return
}
this.$confirm({
content: this.$t('confirmBatchDeletion'),
onOk: () => {
const idList = []
const selectionRows = JSON.parse(JSON.stringify(this.selectionRows))
for (let i = 0; i < selectionRows.length; i++) {
if (selectionRows[i].createBy === this.userInfo.username &&
selectionRows[i].releaseState === 'draft') {
idList.push(selectionRows[i].id)
}
}
if (idList.length > 0) {
postAction(this.url.deleteBatch, { ids: idList.join(',') }).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.selectedRowKeys = []
this.loadData(1)
} else {
this.$message.warning(res.message)
}
})
} else {
this.$message.warning(this.$t('doNotHavePermissionDeleteData'))
}
}
})
},
// 对比结果按钮点击
comparisonResultsClick (item) {
if (item.createBy === this.userInfo.username && item.releaseState === 'draft') {
item.isDisplay = 2
} else {
item.isDisplay = 1
}
this.$router.push({
path: '/docTool/ComparisonResults',
query: {
id: item.id,
isDisplay: item.isDisplay
}
})
},
// 撤回按钮点击
subscribe (record) {
this.$confirm({
content: record.releaseState === 'draft' ? this.$t('confirmRelease') : this.$t('confirmWithdraw'),
onOk: () => {
let releaseState = ''
if (record.releaseState === 'draft') {
releaseState = 'draft'
} else {
releaseState = 'published'
}
const query = {
releaseState: releaseState,
id: record.id
}
withdrawPublication(query).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.loadData(1)
} else {
this.$message.warning(res.message)
}
})
}
})
},
/**
* 编辑
* @param row
*/
edit (row) {
this.$router.push({
path: '/docTool/DocDataComparison',
query: {
id: row.id
}
})
},
/**
* 发起比对
*/
initiatingProcessClick () {
this.$router.push({
path: '/docTool/InitiateComparison'
})
},
/**
* 跳转标准详情
* @param type
* @param record
*/
toDetailPage (type, record) {
let standardId, standardSource, path
// 判断是新旧那个标准点击的,取字段
if (type === 'old') {
standardId = record.standardIdLeft
standardSource = record.standardSourceLeft
}
if (type === 'new') {
standardId = record.standardIdRight
standardSource = record.standardSourceRight
}
// 根据来源跳转不同的详情页
switch (standardSource) {
// 国内
case StandardSource.DOMESTIC.value:
path = '/standardRegulationLibrary/DomesticStandardDetail'
break
// 海外
case StandardSource.OVERSEAS.value:
path = '/standardRegulationLibrary/OverseasStandardDetail'
break
// 企标
case StandardSource.ENTERPRISE.value:
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
break
default:
break
}
if (!path) {
return
}
const query = {
id: standardId
}
this.$openPageNewSheet({
path, query
})
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
/deep/ .table-page-search-wrapper .ant-form-inline .ant-form-item > .ant-form-item-label {
min-width: 80px;
}
</style>
@@ -0,0 +1,778 @@
<template>
<internal-detail-page :title="$t('docTool.comparison.documentComparison')"
:content-padding="0"
:loading="loading"
need-custom-back-func
@back="handleBack">
<template v-slot:titleRightCustom>
<!--查看比对结果-->
<a-button class="header-btn"
type="primary"
icon="eye"
ghost
@click="viewTheComparisonResultsClick"
v-has="'docComparison:dataComparison:viewTheComparisonResults'">
{{ $t('docTool.comparison.viewTheComparisonResults') }}
</a-button>
<!--添加全文评论-->
<a-button class="header-btn"
type="primary"
icon="message"
ghost
@click="addFullTextCommentClick"
v-has="'docComparison:dataComparison:addFullTextComment'">
{{ $t('docTool.comparison.addFullTextComment') }}
</a-button>
<!--关闭/打开自动匹配-->
<a-button class="header-btn"
type="primary"
:icon="isMatching?'close-circle':'check-circle'"
ghost
@click="turnOffAutomaticMatchingClick"
v-has="'docComparison:dataComparison:turnOffAutomaticMatching'">
{{ isMatching ? $t('docTool.comparison.turnOffAutomaticMatching') : $t('docTool.comparison.turnOnAutoMatch') }}
</a-button>
</template>
<div class="can-scroll-content">
<div class="detail-content">
<!--左边的文档-->
<div class="detail-content-left">
<!--左边的目录-->
<div class="standard-catalogue part-common" :class="{'hide-catalogue': !leftCatalogueShow}">
<div class="catalogue-show" @click="changeLeftCatalogueShow">
<a-icon :type="leftCatalogueShow ? 'left' : 'right'" />
</div>
<div class="part-title" v-show="leftCatalogueShow">{{ $t('standardRegulationLibrary.standardCatalogue') }}</div>
<div class="has-title-part-content" v-show="leftCatalogueShow">
<a-tree
class="draggable-tree"
:selected-keys="leftTreeSelectedKeys"
:replace-fields="{key: 'menuId'}"
@select="onSelectLeft"
:tree-data="resultData.menuLeft">
<a-icon slot="switcherIcon" type="down" :style="{ fontSize: '12px', color: '#103770' }" />
<template slot="title" slot-scope="{title}">
<!-- 目录前三个和有子集的标题加粗 -->
<span :style="title.indexOf('.')===-1?'font-weight: 600;':''" :title="title">{{ title }}</span>
</template>
</a-tree>
</div>
</div>
<!--左侧标准信息-->
<div class="detail-content-left-container">
<!--标准名称-->
<div class="standard-name">
<a-tooltip placement="topLeft">
<template #title>
{{ resultData.serialNumberLeft }} {{ resultData.fileNameLeft }}
</template>
<span>{{ resultData.serialNumberLeft }} {{ resultData.fileNameLeft }}</span>
</a-tooltip>
</div>
<div class="detail-content-left-scroll">
<div class="detail-content-left-item" @click="detailLeft(index,item)"
v-for="(item,index) in resultData.textLeft" :key="index"
:class="{'detail-content-item-selected': leftHighlightMenuId === item.menuId && leftHighlightId === item.id,'detail-content-item-saved':item.reviewed + '' === '1'}">
<div class="detail-content-item-text" style="margin-bottom: 10px;">
<span>{{ item.itemsNum }}</span> <span v-html="item.itemsName"></span>
</div>
<div class="detail-content-item-text" v-html="item.itemsText"></div>
</div>
</div>
</div>
</div>
<!--右侧文档-->
<div class="detail-content-right">
<!--右侧目录-->
<div class="standard-catalogue part-common" :class="{'hide-catalogue': !rightCatalogueShow}">
<div class="catalogue-show" @click="changeRightCatalogueShow">
<a-icon :type="rightCatalogueShow ? 'left' : 'right'" />
</div>
<div class="part-title" v-show="rightCatalogueShow">{{ $t('standardRegulationLibrary.standardCatalogue') }}</div>
<div class="has-title-part-content" v-show="rightCatalogueShow">
<a-tree
class="draggable-tree"
:selected-keys="rightTreeSelectedKeys"
:replace-fields="{key: 'menuId'}"
@select="onSelectRight"
:tree-data="resultData.menuRight">
<a-icon slot="switcherIcon" type="down" :style="{ fontSize: '12px', color: '#103770' }" />
<template slot="title" slot-scope="{title}">
<!-- 目录前三个和有子集的标题加粗 -->
<span :style="title.indexOf('.')===-1?'font-weight: 600;':''" :title="title">{{ title }}</span>
</template>
</a-tree>
</div>
</div>
<!--右侧标准信息-->
<div class="detail-content-right-container">
<!--标准名称-->
<div class="standard-name">
<a-tooltip placement="topLeft">
<template #title>
{{ resultData.serialNumberRight }} {{ resultData.fileNameRight }}
</template>
<span>{{ resultData.serialNumberRight }} {{ resultData.fileNameRight }}</span>
</a-tooltip>
</div>
<div class="detail-content-right-scroll">
<div class="detail-content-right-item" @click="detailRight(index,item)"
v-for="(item,index) in resultData.textRight" :key="index"
:class="{'detail-content-item-selected': item.menuId === rightHighlightMenuId && rightHighlightId === item.id, 'detail-content-item-saved':item.reviewed + '' === '1'}">
<div class="detail-content-item-text" style="margin-bottom: 10px;">
<span>{{ item.itemsNum }}</span> <span v-html="item.itemsName"></span>
</div>
<div class="detail-content-item-text" v-html="item.itemsText">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="remarks">
<a-form-model :model="formInline" class="formAdd" :rules="rules" ref="ruleForm">
<div class="box-title-text-remarks">
<div class="title-text-remarks" :title="$t('docTool.comparison.comparisonOfArticles')" style="width: 84px">
<span>{{ $t('docTool.comparison.comparisonOfArticles') }}</span>
</div>
<a-form-model-item class="item-model" prop="comment">
<a-textarea :placeholder="$t('pleaseEnter')+$t('docTool.comparison.comparisonOfArticles')"
v-model="formInline.comment"
:maxLength="500"
:rows="3" />
</a-form-model-item>
</div>
</a-form-model>
</div>
</div>
<div class="submit-button">
<!--一致-->
<a-button style="margin-right: 10px"
type="primary"
@click="consistentAssessmentClick"
v-has="'docComparison:dataComparison:consistentAssessment'">
{{ $t('docTool.comparison.consistentAssessment') }}
</a-button>
<!--保存-->
<a-button type="primary" @click="submit" v-has="'docComparison:dataComparison:save'">{{ $t('preservation') }}</a-button>
</div>
<!--全文评论-->
<comparison-add-full-comment ref="comparisonAddFullComment" />
</internal-detail-page>
</template>
<script>
// 添加全文评论弹框
import ComparisonAddFullComment from './modules/ComparisonAddFullComment.vue'
import InternalDetailPage from '../../../components/InternalDetailPage.vue'
import { docComparsionConsistentAndSave, getComparisonDetail, clauseComparison } from '../../../api/documentToolApi.js'
export default {
name: 'DocumentDataComparison',
components: { ComparisonAddFullComment, InternalDetailPage },
data () {
return {
formInline: {},
loadingText: false,
rules: {},
loading: false,
resultData: {},
isMatching: true, // 自动匹配是否开启
dataLeft: {},
dataRight: {},
pageFrom: null,
leftCatalogueShow: true, // 左侧的标准目录是否展示
rightCatalogueShow: true, // 右侧的标准目录是否展示
leftTreeSelectedKeys: [], // 左侧被选中树节点
rightTreeSelectedKeys: [], // 右侧被选中树节点
leftHighlightMenuId: null, // 左侧高亮数据的menuId
rightHighlightMenuId: null, // 右侧高亮数据的menuId
leftHighlightId: null, // 左侧高亮数据的id
rightHighlightId: null // 右侧高亮数据的id
}
},
mounted () {
this.getData()
},
methods: {
// 文档对比的一致按钮点击
consistentAssessmentClick () {
if (!this.dataLeft.id && !this.dataRight.id) {
this.$message.warning(this.$t('docTool.comparison.pleaseSelectTheDataCompared'))
return
}
const query = {
itemsIdLeft: this.dataLeft.id,
itemsIdRight: this.dataRight.id,
infoId: this.$route.query.id,
comment: '一致'
}
this.loading = true
docComparsionConsistentAndSave(query).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.formInline = {}
// 保存之后会有一个淡蓝色的边框
if (this.leftHighlightMenuId === this.dataLeft.menuId) {
this.leftHighlightMenuId = null
}
if (this.rightHighlightMenuId === this.dataRight.menuId) {
this.rightHighlightMenuId = null
}
this.resultData.textRight.forEach(val => {
if (val.id === this.dataRight.id) {
val.reviewed = '1'
}
})
this.resultData.textLeft.forEach(val => {
if (val.id === this.dataLeft.id) {
val.reviewed = '1'
}
})
this.resultData = { ...this.resultData }
this.dataLeft = {}
this.dataRight = {}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 文档对比的保存按钮点击
submit () {
if (!this.dataLeft.id && !this.dataRight.id) {
this.$message.warning(this.$t('docTool.comparison.pleaseSelectTheDataCompared'))
return
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
const query = {
itemsIdLeft: this.dataLeft.id,
itemsIdRight: this.dataRight.id,
infoId: this.$route.query.id,
comment: this.formInline.comment
}
this.loading = true
docComparsionConsistentAndSave(query).then((res) => {
if (res.success) {
this.$message.success(res.message)
this.formInline = {}
// 保存之后会有一个淡蓝色的边框
if (this.leftHighlightMenuId === this.dataLeft.menuId) {
this.leftHighlightMenuId = null
}
if (this.rightHighlightMenuId === this.dataRight.menuId) {
this.rightHighlightMenuId = null
}
this.resultData.textRight.forEach(val => {
if (val.id === this.dataRight.id) {
val.reviewed = '1'
}
})
this.resultData.textLeft.forEach(val => {
if (val.id === this.dataLeft.id) {
val.reviewed = '1'
}
})
this.resultData = { ...this.resultData }
this.dataLeft = {}
this.dataRight = {}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
}
})
},
// 查看对比结果按钮点击
viewTheComparisonResultsClick () {
this.$router.push({
path: '/docTool/ComparisonResults',
query: {
id: this.$route.query.id,
isDisplay: 2
}
})
},
// 添加全文评论按钮点击
addFullTextCommentClick () {
const query = {
id: this.$route.query.id
}
getComparisonDetail(query).then((res) => {
if (res.success) {
this.$refs.comparisonAddFullComment.open(res.result.comments || '')
}
})
},
// 关闭自动匹配、开启自动匹配
turnOffAutomaticMatchingClick () {
this.isMatching = !this.isMatching
this.$message.success(this.$t('operationSuccessful'))
},
// 获取文件对比详情
getData () {
const query = {
id: this.$route.query.id
}
this.loading = true
getComparisonDetail(query).then((res) => {
if (res.success) {
this.resultData = res.result || {}
} else {
this.resultData = {}
}
}).finally(() => {
this.loading = false
})
},
// 点击左侧文档内容
detailLeft (num, item) {
this.leftTreeSelectedKeys = [item.menuId]
if (item.id === this.dataLeft.id && !this.isMatching) {
this.dataLeft = {}
this.leftHighlightMenuId = null
this.leftHighlightId = null
return
}
this.leftHighlightMenuId = item.menuId
this.leftHighlightId = item.id
this.dataLeft = item
this.dataLeft.numIndex = num
// 自动匹配,右侧数据需要跳转
if (this.isMatching) {
const index = this.dataLeft.targetStand
if (index > -1) {
this.dataRight = this.resultData.textRight[index]
this.dataRight.numIndex = index
this.rightHighlightMenuId = this.dataRight.menuId
this.rightHighlightId = this.dataRight.id
this.rightTreeSelectedKeys = [this.dataRight.menuId]
this.handleJumpRightData(index)
this.clauseComparison()
}
}
},
// 点击右侧文档内容
detailRight (num, item) {
this.rightTreeSelectedKeys = [item.menuId]
if (item.id === this.dataRight.id && !this.isMatching) {
this.dataRight = {}
this.rightHighlightMenuId = null
this.rightHighlightId = null
return
}
this.dataRight = item
this.dataRight.numIndex = num
this.rightHighlightMenuId = item.menuId
this.rightHighlightId = item.id
if (this.isMatching) {
const index = this.dataRight.targetStand
if (index > -1) {
this.dataLeft = this.resultData.textLeft[index]
this.dataLeft.numIndex = index
this.leftHighlightMenuId = this.dataLeft.menuId
this.leftHighlightId = this.dataLeft.id
this.leftTreeSelectedKeys = [this.dataLeft.menuId]
this.handleJumpLeftData(index)
this.clauseComparison()
}
}
},
// 自动匹配
clauseComparison () {
const query = {
leftId: this.dataLeft.id,
rightId: this.dataRight.id
}
this.loadingText = true
clauseComparison(query).then((res) => {
if (res.success) {
this.loadingText = false
this.resultData.textLeft[this.dataLeft.numIndex].itemsText = res.result[0]
this.resultData.textRight[this.dataRight.numIndex].itemsText = res.result[1]
this.resultData.textLeft[this.dataLeft.numIndex].itemsName = res.result[2]
this.resultData.textRight[this.dataRight.numIndex].itemsName = res.result[3]
} else {
this.loadingText = false
}
})
},
// 左侧树选择
onSelectLeft (selectedKeys, exports) {
const node = exports.node.$vnode.data.props.dataRef
const index = this.resultData.textLeft.findIndex(tt => tt.menuId === node.menuId)
this.dataLeft = this.resultData.textLeft[index]
console.log(this.dataLeft)
this.dataLeft.numIndex = index
this.leftHighlightMenuId = this.dataLeft.menuId
this.leftHighlightId = this.dataLeft.id
this.leftTreeSelectedKeys = [this.dataLeft.menuId]
this.handleJumpLeftData(index)
// 处理选中右侧树
if (this.isMatching) {
const indexRight = this.dataLeft.targetStand
if (indexRight > -1) {
this.dataRight = this.resultData.textRight[indexRight]
this.dataRight.numIndex = indexRight
this.rightHighlightMenuId = this.dataRight.menuId
this.rightHighlightId = this.dataRight.id
this.rightTreeSelectedKeys = [this.dataRight.menuId]
this.handleJumpRightData(indexRight)
this.clauseComparison()
}
}
},
// 右侧树选中
onSelectRight (selectedKeys, exports) {
const node = exports.node.$vnode.data.props.dataRef
const index = this.resultData.textRight.findIndex(tt => tt.menuId === node.menuId)
this.dataRight = this.resultData.textRight[index]
this.dataRight.numIndex = index
this.rightHighlightMenuId = this.dataRight.menuId
this.rightHighlightId = this.dataRight.id
this.rightTreeSelectedKeys = [this.dataRight.menuId]
this.handleJumpRightData(index)
// 处理选中左侧树
if (this.isMatching) {
const indexLeft = this.dataRight.targetStand
if (indexLeft > -1) {
this.dataLeft = this.resultData.textLeft[indexLeft]
this.dataLeft.numIndex = indexLeft
this.leftHighlightMenuId = this.dataLeft.menuId
this.leftHighlightId = this.dataLeft.Id
this.leftTreeSelectedKeys = [this.dataLeft.menuId]
this.handleJumpLeftData(indexLeft)
this.clauseComparison()
}
}
},
/**
* 左侧数据跳转
*/
handleJumpLeftData (index) {
if (index > -1) {
const scrollContent = document.getElementsByClassName('detail-content-left-scroll')
const clauseItemDom = document.getElementsByClassName('detail-content-left-item')
scrollContent[0].scrollTop = clauseItemDom[index].offsetTop - 150
}
},
/**
* 右侧数据跳转
*/
handleJumpRightData (index) {
if (index > -1) {
const detailContent = document.getElementsByClassName('detail-content-right-scroll')
const detailRight = document.getElementsByClassName('detail-content-right-item')
detailContent[0].scrollTop = detailRight[index].offsetTop - 150
}
},
handleBack () {
this.$router.push({
path: '/docTool/docComparison'
})
},
changeLeftCatalogueShow () {
this.leftCatalogueShow = !this.leftCatalogueShow
},
changeRightCatalogueShow () {
this.rightCatalogueShow = !this.rightCatalogueShow
}
}
}
</script>
<style scoped lang="less">
.header-btn {
margin-left: 12px;
}
.header-btn:first-child {
margin-left: 0;
}
.can-scroll-content {
height: calc(100% - 52px);
overflow: auto;
}
.detail-content {
height: calc(100% - 131px);
background-color: #eff1f4;
}
.detail-content-left {
width: 50%;
float: left;
height: 100%;
border-right: 2px #EFF1F3 solid;
box-sizing: border-box;
border-bottom: 2px #EFF1F3 solid;
display: flex;
}
.detail-content-right {
width: 50%;
height: 100%;
float: left;
box-sizing: border-box;
border-bottom: 2px #EFF1F3 solid;
display: flex;
}
.detail-content-left-header {
width: 100%;
font-size: 16px;
font-family: Blue Sky Noto;
font-weight: 400;
color: #040B29;
margin-bottom: 24px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.box-title-text-remarks {
line-height: 1.4;
display: flex;
/*align-items: center;*/
margin-bottom: 10px;
}
.item-model {
width: calc(100% - 232px);
}
.title-text-remarks {
width: 68px;
color: #000F16;
display: inline-block;
font-weight: 500;
font-size: 14px;
margin-right: 16px;
margin-top: 6px;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.box-button {
height: 38px;
/*margin-top: 2px;*/
}
.remarks {
margin-top: 20px;
width: 50%;
display: inline-block;
padding: 0 32px;
box-sizing: border-box;
}
.itemModel {
width: calc(100% - 232px);
}
.submit-button {
height: 52px;
text-align: right;
padding: 10px 24px;
}
.detail-content-left-container {
height: 100%;
overflow-y: auto;
transition: all 1s;
scroll-behavior: smooth;
flex: 1;
}
.detail-content-right-container {
height: 100%;
overflow-y: auto;
transition: all 1s;
scroll-behavior: smooth;
flex: 1;
.standard-name {
margin-right: 0;
}
}
.detail-content-left-scroll {
height: calc(100% - 25px - 40px);
width: calc(100% - 20px);
display: inline-block;
margin: 0 10px 0 10px;
overflow: auto;
background-color: white;
}
.detail-content-right-scroll {
height: calc(100% - 25px - 40px);
width: calc(100% - 10px);
display: inline-block;
margin-left: 10px;
overflow: auto;
background-color: white;
}
.detail-content-left-item {
padding: 16px 20px;
box-sizing: border-box;
border: 1px solid #EFF1F3;
cursor: pointer;
background-color: white;
}
.detail-content-right-item {
padding: 16px 20px;
box-sizing: border-box;
border: 1px solid #EFF1F3;
cursor: pointer;
background-color: white;
}
.detail-content-left-item:last-child {
margin-bottom: 0;
}
.detail-content-item-text {
font-size: 16px;
font-weight: 400;
color: #040B29;
word-break: break-word;
/deep/ table {
width: 100%;
}
/deep/ img {
max-width: 100%;
}
}
.detail-content-item-selected {
border: 2px red solid !important;
}
.detail-content-item-saved {
border: 2px #21c9cc solid;
}
::v-deep .delClass {
color: #ff7168;
}
::v-deep .insertClass {
color: green;
}
/deep/ .ant-tree li span.ant-tree-switcher {
float: right;
}
::-webkit-scrollbar {
width: 3px;
}
.part-common {
border-radius: 8px;
background: #FFFFFF;
}
.standard-catalogue {
height: calc(100% - 20px);
width: 200px;
margin: 10px 10px 10px 0;
position: relative;
transition: width .2s linear;
/deep/ .ant-tree li .ant-tree-node-content-wrapper{
max-width: calc(100% - 24px);
&:hover {
max-width: 100%;
}
}
/deep/ .ant-tree li .ant-tree-node-content-wrapper.ant-tree-node-selected {
background-color: transparent !important;
color: @primary-color;
}
.catalogue-show {
position: absolute;
width: 12px;
height: 24px;
left: 100%;
top: 50%;
transform: translateY(-50%);
background-color: @primary-color;
border-radius: 0 12px 12px 0;
cursor: pointer;
color: white;
.anticon {
left: -2px;
position: absolute;
top: 50%;
transform: translateY(-50%);
}
}
}
.hide-catalogue {
width: 0;
min-width: 0;
}
.part-title {
height: 40px;
background: rgba(213, 44, 38, 0.12);
border-radius: 8px 8px 0 0;
line-height: 40px;
padding: 0 24px;
font-size: 16px;
font-family: PingFang SC-Semibold, PingFang SC, sans-serif;
font-weight: 600;
color: #1D2129;
white-space: nowrap;
overflow: hidden;
}
.has-title-part-content {
height: calc(100% - 40px);
overflow: auto;
padding: 0 20px;
}
.standard-name {
margin: 10px 10px 5px 10px;
height: 40px;
background: #FFFFFF;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24px;
}
.standard-name > span {
font-size: 16px;
font-family: PingFang SC-Semibold, PingFang SC, sans-serif;
font-weight: 600;
color: #1D2129;
flex: 1;
width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
</style>
@@ -0,0 +1,581 @@
<template>
<internal-detail-page :title="$t('docTool.comparison.initiateComparison')" :content-padding="0" :loading="loading">
<div class="can-scroll-content">
<div class="detail-content-box">
<div class="detail-content-left">
<a-form layout="inline" @keyup.enter.native="searchQueryLeft">
<a-row>
<a-col :md="8" :sm="8">
<a-form-item :label="$t('number')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('number')"
v-model="queryParamLeft.serialNumber"></a-input>
</a-form-item>
</a-col>
<a-col :md="8" :sm="8">
<a-form-item :label="$t('title')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('title')"
v-model="queryParamLeft.title"></a-input>
</a-form-item>
</a-col>
<span style="text-align:right;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="8" :sm="24">
<a-button type="primary" @click="searchQueryLeft" v-has="'docComparsion:initiate:search'">
{{ $t('query') }}
</a-button>
<a-button style="margin-left: 8px" @click="searchResetLeft">{{ $t('reset') }}</a-button>
</a-col>
</span>
</a-row>
</a-form>
<j-table
ref="table"
size="middle"
:can-drag="true"
:loading="loadingLeft"
:pagination="false"
:scroll="{x: '100%',y:340}"
rowKey="id"
:data-source="dataSourceLeft"
:row-selection="{ selectedRowKeys: selectedRowKeysLeft, onChange: onSelectChangeLeft, columnTitle:' ', type: 'radio'}"
:columns="columns"
@change="tableChangeLeft"
>
</j-table>
<div class="box-button-standard">
<span class="box-button-standard-left" :title="$t('docTool.comparison.selectedStandard')">
{{ $t('docTool.comparison.selectedStandard') }}
</span>
<span class="box-button-standard-right"
@click="selectLeftClick(selectedRowsLeft[0])"
:title="selectedRowsLeft[0]?selectedRowsLeft[0].serialNumber+' '+selectedRowsLeft[0].fileName:$t('docTool.comparison.noComparisonDocumentSelected')"
:class="{'box-button-standard-right-color':selectedRowsLeft[0]}">
{{
selectedRowsLeft[0] ? selectedRowsLeft[0].serialNumber + ' ' + selectedRowsLeft[0].fileName : $t('docTool.comparison.noComparisonDocumentSelected')
}}
</span>
</div>
</div>
<div class="detail-content-right">
<a-form layout="inline" @keyup.enter.native="searchQueryRight">
<a-row>
<a-col :md="8" :sm="8">
<a-form-item :label="$t('number')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('number')"
v-model="queryParamRight.serialNumber"></a-input>
</a-form-item>
</a-col>
<a-col :md="8" :sm="8">
<a-form-item :label="$t('title')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('title')"
v-model="queryParamRight.title"></a-input>
</a-form-item>
</a-col>
<span style="text-align:right;overflow: hidden;" class="table-page-search-submitButtons">
<a-col :md="8" :sm="24">
<a-button type="primary" @click="searchQueryRight" v-has="'docComparsion:initiate:search'">
{{ $t('query') }}
</a-button>
<a-button style="margin-left: 8px" @click="searchResetRight">{{ $t('reset') }}</a-button>
</a-col>
</span>
</a-row>
</a-form>
<j-table
ref="table"
size="middle"
:can-drag="true"
:loading="loadingRight"
:pagination="false"
:scroll="{x: '100%',y:340}"
rowKey="id"
:data-source="dataSourceRight"
:row-selection="{ selectedRowKeys: selectedRowKeysRight, onChange: onSelectChangeRight, columnTitle:' ', type: 'radio'}"
:columns="tableColumns"
@change="tableChangeRight"
>
</j-table>
<div class="box-button-standard">
<span class="box-button-standard-left" :title="$t('docTool.comparison.selectedStandard')">
{{ $t('docTool.comparison.selectedStandard') }}
</span>
<span class="box-button-standard-right"
@click="selectLeftClick(selectedRowsRight[0])"
:title="selectedRowsRight[0]?selectedRowsRight[0].serialNumber+' '+selectedRowsRight[0].fileName:$t('docTool.comparison.noComparisonDocumentSelected')"
:class="{'box-button-standard-right-color':selectedRowsRight[0]}">
{{
selectedRowsRight[0] ? selectedRowsRight[0].serialNumber + ' ' + selectedRowsRight[0].fileName : $t('docTool.comparison.noComparisonDocumentSelected')
}}
</span>
</div>
</div>
</div>
<div class="remarks">
<a-form-model :model="formInline" class="formAdd" :rules="rules" ref="ruleForm">
<div class="box-title-text-remarks">
<div class="title-text-remarks" :title="$t('docTool.comparison.remarkInfo')">
<span>{{ $t('docTool.comparison.remarkInfo') }}</span>
</div>
<a-form-model-item class="item-model" prop="remark">
<a-textarea :placeholder="$t('pleaseEnter')+$t('docTool.comparison.remarkInfo')"
:maxLength="500"
v-model="formInline.remark"
:rows="4" />
</a-form-model-item>
</div>
</a-form-model>
</div>
</div>
<div class="box-button">
<!--发起文档对比-->
<a-button type="primary" @click="submit" v-has="'docComparsion:initiate:initiateComparison'">
{{ $t('docTool.comparison.initiateDocumentComparison') }}
</a-button>
</div>
<div id="images">
<div class="image" v-viewer="{movable: false}">
<img v-show="image" :src="imageUrl">
</div>
</div>
</internal-detail-page>
</template>
<script>
import JTable from '../../../components/jero/JTable.vue'
import { downloadFile, getFileAccessHttpUrl } from '../../../api/manage.js'
import { previewPdf } from '../../../utils/previewPdf.js'
import { Base64 } from 'js-base64'
import { mapGetters } from 'vuex'
import InternalDetailPage from '../../../components/InternalDetailPage.vue'
import { getInitComparsionPageData, submitInitComparsion } from '../../../api/documentToolApi.js'
export default {
name: 'InitiateComparison',
components: { JTable, InternalDetailPage },
data () {
return {
loading: false,
labelCol: {
span: 6
},
wrapperCol: {
span: 18
},
loadingLeft: false,
queryParamLeft: {},
ipaginationLeft: {
current: 1,
pageSize: 50,
pageSizeOptions: ['10', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + ' ' + total + ' ' + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
selectedRowKeysLeft: [],
selectedRowsLeft: [],
dataSourceLeft: [],
loadingRight: false,
queryParamRight: {},
ipaginationRight: {
current: 1,
pageSize: 50,
pageSizeOptions: ['10', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + ' ' + total + ' ' + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
selectedRowKeysRight: [],
dataSourceRight: [],
selectedRowsRight: [],
formInline: {},
downLoadFileUrl: window._CONFIG.domianPreviewURL + '/sys/common/download',
downLoadImgUrl: window._CONFIG.domianWebImgURL + '/sys/common/download',
rules: {
// remark: [
// {
// max: 300,
// message: this.$t('RemarkInfo') + this.$t('cannotExceed') + 300 + this.$t('Characters'),
// trigger: 'blur'
// }
// ]
},
columns: [
{
title: this.$t('number'),
width: 60,
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{
title: this.$t('number'),
ellipsis: true,
dataIndex: 'serialNumber',
width: 100
},
{
title: this.$t('title'),
ellipsis: true,
dataIndex: 'title',
width: 100
},
{
title: this.$t('docTool.comparison.textStatus'),
ellipsis: true,
dataIndex: 'fileType_dictText',
width: 100
},
{
title: this.$t('fileName'),
ellipsis: true,
dataIndex: 'fileName',
width: 100
}
],
tableColumns: [
{
title: this.$t('number'),
width: 60,
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{
title: this.$t('number'),
ellipsis: true,
dataIndex: 'serialNumber',
width: 100
},
{
title: this.$t('title'),
ellipsis: true,
dataIndex: 'title',
width: 100
},
{
title: this.$t('docTool.comparison.textStatus'),
ellipsis: true,
dataIndex: 'fileType_dictText',
width: 100
},
{
title: this.$t('fileName'),
ellipsis: true,
dataIndex: 'fileName',
width: 100
}
],
image: false,
imageUrl: ''
}
},
mounted () {
this.getListLeft()
this.getListRight()
},
methods: {
...mapGetters(['userInfo']),
goBack () {
this.$router.push({
path: '/docTool/docComparison'
})
},
searchQueryLeft () {
this.ipaginationLeft.current = 1
this.getListLeft()
},
searchResetLeft () {
this.queryParamLeft = {}
this.ipaginationLeft.current = 1
this.getListLeft()
},
onSelectChangeLeft (value, record) {
this.selectedRowKeysLeft = value
// this.selectedRowsLeft = []
// if (this.selectedRowKeysLeft.length > 1) {
// this.selectedRowKeysLeft.shift()
// }
// const content = record.filter(res => {
// return res.id === this.selectedRowKeysLeft[0]
// })
this.selectedRowsLeft = record
},
tableChangeLeft (pagination) {
this.ipaginationLeft = pagination
this.getListLeft()
},
searchQueryRight () {
this.ipaginationRight.current = 1
this.getListRight()
},
searchResetRight () {
this.queryParamRight = {}
this.ipaginationRight.current = 1
this.getListRight()
},
onSelectChangeRight (value, record) {
this.selectedRowKeysRight = value
// this.selectedRowsRight = []
// if (this.selectedRowKeysRight.length > 1) {
// this.selectedRowKeysRight.shift()
// }
// const content = record.filter(res => {
// return res.id === this.selectedRowKeysRight[0]
// })
this.selectedRowsRight = record
},
tableChangeRight (pagination) {
this.ipaginationRight = pagination
this.getListRight()
},
submit () {
if (!this.selectedRowsRight[0] || !this.selectedRowsLeft[0]) {
this.$message.warning(this.$t('docTool.comparison.pleaseSelectTheDataCompared'))
return
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.loading = true
const query = {
leftStandard: this.selectedRowsLeft[0].id,
rightStandard: this.selectedRowsRight[0].id,
remark: this.formInline.remark
}
submitInitComparsion(query).then((res) => {
if (res.success) {
console.log(res)
this.loading = false
this.$message.success(this.$t('operationSuccessful'))
this.$router.push({
path: '/docTool/DocDataComparison',
query: {
id: res.result
}
})
} else {
this.loading = false
this.$message.warning(res.message)
}
})
}
})
},
getListLeft () {
const queryParam = JSON.parse(JSON.stringify(this.queryParamLeft))
Object.keys(queryParam).forEach(val => {
if (queryParam[val] instanceof Array) {
queryParam[val] = queryParam[val].join(',')
}
})
const query = {
pageNo: this.ipaginationLeft.current,
pageSize: this.ipaginationLeft.pageSize,
fileId: '1',
splitStatus: '2',
...queryParam
}
this.loadingLeft = true
getInitComparsionPageData(query).then((res) => {
if (res.success) {
if (res.result.current > 1 && res.result.records.length === 0) {
this.ipaginationLeft.current = 1
this.getListLeft()
return
}
this.dataSourceLeft = res.result.records || []
this.ipaginationLeft.total = res.result.total
}
}).finally(() => {
this.loadingLeft = false
})
},
selectLeftClick (fileQuery) {
if (fileQuery) {
const fileName = fileQuery.fileName
const index1 = fileName.lastIndexOf('.')
const index2 = fileName.length
const fileSuffix = fileName.substring(index1, index2)
if (fileSuffix === '.pdf') {
// window.open('/pdf/web/viewer.html?file=' + encodeURIComponent('/jero-boot/sys/common/pdf/viewFile?id=' + fileQuery.fileId + '&userName=' + this.userInfo().username))
const url = previewPdf(fileQuery.fileId)
window.open(url)
} else if (fileSuffix === '.docx' || fileSuffix === '.doc') {
const url = window._CONFIG.onlinePreviewDomainURL + '?url=' + Base64.encode(this.downLoadFileUrl + '/' + fileQuery.fileId + fileSuffix)
window.open(url, '_blank')
} else if (fileSuffix === '.xlsx' || fileSuffix === '.xls') {
const url = window._CONFIG.onlinePreviewDomainURL + '?url=' + Base64.encode(this.downLoadFileUrl + '/' + fileQuery.fileId + fileSuffix)
window.open(url, '_blank')
} else if (fileSuffix === '.png' || fileSuffix === '.jpeg' || fileSuffix === '.gif' || fileSuffix === '.jpg') {
// const url = window._CONFIG.onlinePreviewDomainURL + '?url=' + Base64.encode(this.downLoadImgUrl + '/' + fileQuery.fileId + fileSuffix)
// window.open(url, '_blank')
this.image = true
this.imageUrl = getFileAccessHttpUrl(fileQuery.fileId)
// 获取viewer实例
const viewer = this.$el.querySelector('.image').$viewer
// 调用show方法进行显示预览图
viewer.show()
} else {
downloadFile('/sys/common/downLoadFile', fileQuery.fileName, { id: fileQuery.fileId })
}
}
},
getListRight () {
const queryParam = JSON.parse(JSON.stringify(this.queryParamRight))
Object.keys(queryParam).forEach(val => {
if (queryParam[val] instanceof Array) {
queryParam[val] = queryParam[val].join(',')
}
})
const query = {
pageNo: this.ipaginationRight.current,
pageSize: this.ipaginationRight.pageSize,
fileId: '1',
splitStatus: '2',
...queryParam
}
this.loadingRight = true
getInitComparsionPageData(query).then((res) => {
if (res.success) {
if (res.result.current > 1 && res.result.records.length === 0) {
this.pageNoRight = 1
this.getListRight()
return
}
this.dataSourceRight = res.result.records || []
this.ipaginationRight.total = res.result.total
}
}).finally(() => {
this.loadingRight = false
})
}
}
}
</script>
<style scoped lang="less">
@import '~../../../assets/less/common.less';
.can-scroll-content {
height: calc(100% - 52px);
overflow: auto;
}
.detail-content-box {
display: flex;
height: calc(100% - 152px);
min-height: calc(340px + 46px + 40px + 72px + 48px);
}
.detail-content-left {
width: 50%;
border-right: 2px #EFF1F3 solid;
padding: 24px 32px;
box-sizing: border-box;
border-bottom: 2px #EFF1F3 solid;
height: 100%;
}
.detail-content-right {
width: 50%;
padding: 24px 32px;
box-sizing: border-box;
border-bottom: 2px #EFF1F3 solid;
height: 100%;
}
.box-title-text-remarks {
line-height: 1.4;
display: flex;
margin-bottom: 10px;
}
.title-text-remarks {
width: 68px;
color: #000F16;
display: inline-block;
font-weight: 500;
font-size: 14px;
margin-right: 16px;
margin-top: 6px;
text-align: right;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.box-button {
padding: 10px 24px;
text-align: right;
}
.box-button-standard {
width: 100%;
margin-top: 20px;
}
.box-button-standard-left {
width: 111px;
height: 46px;
padding: 0 6px;
box-sizing: border-box;
display: inline-block;
border: 1px solid #CED0D8;
border-right: none;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
line-height: 46px;
color: #040B29;
}
.box-button-standard-right {
width: calc(100% - 111px);
height: 46px;
padding: 0 4px;
box-sizing: border-box;
display: inline-block;
border: 1px solid #CED0D8;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-align: center;
line-height: 46px;
color: #9B9DA9;
}
.remarks {
margin-top: 20px;
width: 50%;
padding: 0 32px;
box-sizing: border-box;
}
.item-model {
width: calc(100% - 232px);
}
.submit-button {
width: 100%;
height: 60px;
text-align: right;
padding: 14px 32px 0;
box-sizing: border-box;
border-top: 2px #eff1f3 solid;
background: #ffffff;
position: fixed;
bottom: 0;
}
.box-button-standard-right-color {
color: #040B29;
cursor: pointer;
}
</style>
@@ -0,0 +1,254 @@
<template>
<div>
<a-drawer
:title="$t('docTool.comparison.comment')"
:maskClosable="false"
:width="482"
placement="right"
:closable="true"
@close="handleCancel"
:visible="visible">
<div class="button-box">
<a-input-search class="box-input box-input-search" allowClear
@search="allowClearChange"
:placeholder="$t('pleaseEnter')+$t('docTool.comparison.comment')"
v-model="commentContent"></a-input-search>
<a-button @click="submitQuery" style="margin-right: 8px;" type="primary">{{ $t('query') }}</a-button>
<!--发表评论-->
<a-button @click="handleSubmit" :title="$t('docTool.comparison.publishComment')" type="primary">
{{ $t('docTool.comparison.publishComment') }}
</a-button>
</div>
<div class="comment-box" v-if="dataList && dataList.length > 0">
<div class="comment-box-top-box" v-for="(item,index) in dataList" :key="index">
<div class="comment-item">
<div class="comment-item-img">
<img src="../../../../assets/image/daiban.png" class="img" alt="">
</div>
<div class="comment-item-content">
<div class="comment-item-title">
<a-tooltip>
<template #title>{{ item.name }}</template>
<div class="comment-item-title-name">{{ item.name }}</div>
</a-tooltip>
<div class="comment-item-title-time">{{ item.createTime }}</div>
<div class="comment-item-title-icon" @click="messageClick(item)"><a-icon type="message" /></div>
</div>
<div class="comment-item-content-text">{{ item.content }}</div>
</div>
</div>
<div class="inside-box" v-if="item.resComVoList && item.resComVoList.length > 0">
<div class="comment-item inside-comment-item" v-for="(val,indexOne) in item.resComVoList" :key="indexOne">
<div class="comment-item-img">
<img src="../../../../assets/image/daiban.png" class="img" alt="">
</div>
<div class="comment-item-content">
<div class="comment-item-title">
<a-tooltip>
<template #title>{{ val.name }}</template>
<div class="comment-item-title-name">{{ val.name }}</div>
</a-tooltip>
<div class="comment-item-title-time">{{ val.createTime }}</div>
<div class="comment-item-title-icon" @click="messageClick(item)"><a-icon type="message" /></div>
</div>
<div class="comment-item-content-text">{{ val.content }}</div>
</div>
</div>
</div>
</div>
</div>
<div class="comment-box-text" v-else>{{ $t('docTool.comparison.noComment') }}</div>
</a-drawer>
<!--发表评论-->
<comparison-add-reply-comment ref="comparisonAddReplyComment" @ok="getList" />
</div>
</template>
<script>
import { getCommentList } from '../../../../api/documentToolApi.js'
// 添加评论和回复评论弹框
import ComparisonAddReplyComment from './ComparisonAddReplyComment.vue'
export default {
name: 'CommentList',
components: { ComparisonAddReplyComment },
data () {
return {
visible: false,
dataList: [],
commentContent: '',
loading: false
}
},
methods: {
handleCancel () {
this.visible = false
},
getData () {
this.visible = true
this.commentContent = ''
this.getList()
},
handleSubmit () {
this.$refs.comparisonAddReplyComment.title = this.$t('docTool.comparison.publishComment')
this.$refs.comparisonAddReplyComment.open({})
},
allowClearChange () {
this.getList()
},
submitQuery () {
this.getList()
},
getList () {
this.loading = true
getCommentList({
id: this.$route.query.id,
str: this.commentContent
}).then((res) => {
if (res.success) {
this.dataList = res.result || []
} else {
this.dataList = []
}
}).finally(() => {
this.loading = false
})
},
messageClick (row) {
const obj = {}
obj.projectCommentId = row.id
this.$refs.comparisonAddReplyComment.title = this.$t('docTool.comparison.replyToComments')
this.$refs.comparisonAddReplyComment.open(obj)
}
}
}
</script>
<style scoped lang="less">
.comment-box {
position: absolute;
left: 0;
height: calc(100% - 140px);
width: 100%;
overflow: auto;
padding: 0 24px 24px 24px;
box-sizing: border-box;
}
.comment-box-top-box {
padding: 30px 24px 24px 24px;
box-sizing: border-box;
border: 1px solid #F1F2F4;
border-radius: 4px;
margin-bottom: 24px;
}
.img-box {
width: 32px;
height: 32px;
border-radius: 50%;
display: inline-block;
margin-right: 8px;
position: absolute;
.img {
width: 100%;
height: 100%;
}
}
.inside-box {
background: #F6F7FA;
border-radius: 4px;
margin-left: 40px;
width: calc(100% - 40px);
padding: 18px;
box-sizing: border-box;
margin-top: 15px;
}
.button-box {
text-align: right;
margin-bottom: 24px;
}
.comment-box-text {
text-align: center;
margin-top: 40px;
}
.box-input {
width: calc(100% - 183px);
margin-right: 8px;
}
.box-input-search {
/deep/ .ant-input-search-icon {
display: none;
}
}
.comment-item {
display: flex;
&-img {
width: 32px;
height: 32px;
border-radius: 50%;
margin-right: 8px;
.img {
width: 100%;
height: 100%;
}
}
&-content {
flex: 1;
width: 0;
&-text {
font-size: 14px;
font-weight: 400;
color: #040B29;
word-break: break-word;
}
}
&-title {
width: 100%;
display: flex;
&-name {
font-size: 14px;
font-weight: bold;
color: #040B29;
margin-right: 8px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
&-time {
flex-shrink: 0;
font-size: 14px;
font-weight: 400;
color: #6F7385;
margin-right: 8px;
}
&-icon {
flex-shrink: 0;
margin-left: auto;
}
}
}
.inside-comment-item {
margin-bottom: 15px;
}
</style>
@@ -0,0 +1,82 @@
<template>
<j-modal
:title="$t('docTool.comparison.fullTextComments')"
:width="600"
:visible="visible"
:confirm-loading="confirmLoading"
:maskClosable="false"
switchFullscreen
@cancel="visible = false">
<template slot="footer">
<a-button type="primary" :loading="confirmLoading" @click="fullTextCommentsSubmit">
{{ $t('submit') }}
</a-button>
</template>
<a-form-model :model="formInlineText" class="formAdd" :rules="rulesText" ref="ruleFormText">
<a-form-model-item prop="comment"
:labelCol="labelCol" :wrapperCol="wrapperCol"
:label="$t('docTool.comparison.fullTextComments')">
<a-textarea :placeholder="$t('pleaseEnter')+' '+$t('docTool.comparison.fullTextComments')"
v-model="formInlineText.comment"
:maxLength="500"
:rows="4" />
</a-form-model-item>
</a-form-model>
</j-modal>
</template>
<script>
import { addFullTextComment } from '../../../../api/documentToolApi.js'
export default {
name: 'ComparisonAddFullComment',
data () {
return {
visible: false, // 全文评论弹框是否显示
confirmLoading: false,
formInlineText: {},
rulesText: {},
labelCol: {
span: 5
},
wrapperCol: {
span: 18
}
}
},
methods: {
open (comments) {
this.formInlineText.comment = comments
this.visible = true
},
// 添加全文评论弹框的提交按钮
fullTextCommentsSubmit () {
this.$refs.ruleFormText.validate(valid => {
if (valid) {
const formInlineText = Object.assign({}, this.formInlineText)
this.confirmLoading = true
const query = {
id: this.$route.query.id,
comments: formInlineText.comment
}
addFullTextComment(query).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.visible = false
} else {
this.$message.warning(this.$t('operationFailed'))
}
}).finally(() => {
this.confirmLoading = false
})
}
})
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,117 @@
<template>
<j-modal
:title="title"
:width="500"
:visible="visible"
:confirm-loading="confirmLoading"
:maskClosable="false"
@ok="handleOkComment"
switchFullscreen
@cancel="handleCancelComment"
>
<a-form-model :model="formInline" :rules="rules" ref="ruleForm">
<a-row :gutter="24">
<a-col :span="24">
<div class="box-title-text">
<div class="title-text">
<span class="required">*</span>
<span class="title-text-text" :title="title">{{ title }}</span>
</div>
<a-form-model-item v-if="title === $t('docTool.comparison.replyToComments')" class="item-model" :prop="'replyContent'">
<a-textarea
:placeholder="$t('pleaseEnter')+title"
:disabled="false"
:maxLength="500"
v-model="formInline.replyContent"
:autoSize="{ minRows: 2, maxRows: 6 }" />
</a-form-model-item>
<a-form-model-item v-else-if="title === $t('docTool.comparison.publishComment')" class="item-model" :prop="'commentContent'">
<a-textarea
:placeholder="$t('pleaseEnter')+title"
:disabled="false"
:maxLength="500"
v-model="formInline.commentContent"
:autoSize="{ minRows: 2, maxRows: 6 }" />
</a-form-model-item>
</div>
</a-col>
</a-row>
</a-form-model>
</j-modal>
</template>
<script>
import { addComment } from '../../../../api/documentToolApi.js'
export default {
name: 'ComparisonAddReplyComment',
data () {
return {
title: this.$t('docTool.comparison.comment'),
visible: false,
confirmLoading: false,
formInline: {},
rules: {
commentContent: [
{
required: true,
message: this.$t('docTool.comparison.publishComment') + this.$t('cannotEmpty'),
trigger: 'blur'
}
],
replyContent: [
{
required: true,
message: this.$t('docTool.comparison.replyToComments') + this.$t('cannotEmpty'),
trigger: 'blur'
}
]
}
}
},
methods: {
open (formInLine) {
this.visible = true
this.formInline = Object.assign({}, formInLine)
},
handleOkComment () {
this.$refs.ruleForm.validate(valid => {
if (valid) {
let query = {}
if (this.title === this.$t('docTool.comparison.publishComment')) {
query = {
comment: this.formInline.commentContent,
infoId: this.$route.query.id
}
} else if (this.title === this.$t('docTool.comparison.replyToComments')) {
query = {
comment: this.formInline.replyContent,
parentId: this.formInline.projectCommentId,
infoId: this.$route.query.id
}
}
this.confirmLoading = true
addComment(query).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.visible = false
this.$emit('ok')
} else {
this.$message.warning(this.$t('operationFailed'))
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
handleCancelComment () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
@import '~../../../../assets/less/common.less';
</style>
@@ -0,0 +1,135 @@
<template>
<j-modal
:title="title"
:width="600"
:visible="visible"
:confirm-loading="confirmLoading"
:maskClosable="false"
switchFullscreen
@cancel="visible = false"
>
<template slot="footer">
<a-button key="back" @click="visible = false">
{{ $t('cancel') }}
</a-button>
<a-button type="primary" :loading="confirmLoading" @click="fullTextCommentsSubmit">
{{ $t('submit') }}
</a-button>
</template>
<a-form-model :model="formInlineText" class="formAdd" :rules="rulesText" ref="ruleFormText">
<a-form-model-item class="item-model"
prop="comment"
v-if="title === $t('docTool.comparison.fullTextComments')"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('docTool.comparison.fullTextComments')">
<a-textarea :placeholder="$t('pleaseEnter') + $t('docTool.comparison.fullTextComments')"
v-model="formInlineText.comment"
:maxLength="500"
:rows="4" />
</a-form-model-item>
<a-form-model-item class="item-model"
prop="comment"
v-if="title === $t('docTool.comparison.comparativeComments')"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
:label="$t('docTool.comparison.comparativeComments')">
<a-textarea :placeholder="$t('pleaseEnter')+$t('docTool.comparison.comparativeComments')"
v-model="formInlineText.comment"
:maxLength="500"
:rows="4" />
</a-form-model-item>
</a-form-model>
</j-modal>
</template>
<script>
import { putAction } from '../../../../api/manage.js'
import { addFullTextComment, editComparisonComment } from '../../../../api/documentToolApi.js'
export default {
name: 'ComparisonResultEditModal',
data () {
return {
title: '',
visible: false,
confirmLoading: false,
formInlineText: {},
rulesText: {},
labelCol: {
span: 5
},
wrapperCol: {
span: 18
}
}
},
methods: {
open (form) {
this.initData()
this.visible = true
this.formInlineText = Object.assign({}, form)
},
initData () {
this.$nextTick(() => {
this.$refs.ruleFormText.clearValidate()
})
},
// 发表评论弹框的提交按钮点击
fullTextCommentsSubmit () {
this.$refs.ruleFormText.validate(valid => {
if (valid) {
if (this.title === this.$t('docTool.comparison.fullTextComments')) {
this.fullTextComments()
} else {
this.comparaComments()
}
}
})
},
// 全文评论提交
fullTextComments () {
this.confirmLoading = true
const query = {
id: this.$route.query.id,
comments: this.formInlineText.comment
}
addFullTextComment(query).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.visible = false
this.$emit('ok')
} else {
this.$message.warning(this.$t('operationFailed'))
}
}).finally(() => {
this.confirmLoading = false
})
},
// 发表评论提交
comparaComments () {
this.confirmLoading = true
const query = {
infoId: this.$route.query.id,
id: this.formInlineText.id,
comment: this.formInlineText.comment
}
editComparisonComment(query).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.visible = false
this.$emit('ok')
} else {
this.$message.warning(this.$t('operationFailed'))
}
}).finally(() => {
this.confirmLoading = false
})
}
}
}
</script>
<style scoped lang="less">
@import '~../../../../assets/less/common.less';
</style>
@@ -0,0 +1,388 @@
<template>
<a-card :bordered="false">
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<!--标准号/标准名称-->
<a-col :md="6" :sm="12">
<a-form-item :label="$t('standardSelect.standardNumOrName')">
<a-input :placeholder="$t('pleaseEnter') + $t('standardSelect.standardNumOrName')"
v-model="queryParam.serialNumberOrName"></a-input>
</a-form-item>
</a-col>
<!--拆分状态-->
<a-col :md="6" :sm="12">
<a-form-item :label="$t('docTool.split.splitState')">
<j-dict-select-tag
class="box-input"
v-model="queryParam.splitStatus"
:placeholder="$t('pleaseSelect') + $t('docTool.split.splitState')"
:type="'select'"
:triggerChange="false"
dictCode="split_status" />
</a-form-item>
</a-col>
<!--编辑人-->
<a-col :md="6" :sm="12">
<a-form-item :label="$t('docTool.split.editor')">
<a-input :placeholder="$t('pleaseEnter') + $t('docTool.split.editor')" v-model="queryParam.author"></a-input>
</a-form-item>
</a-col>
<div style="float: right;overflow: hidden;margin-right: 11px" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search" style="margin-left: 8px" v-has="'split:sarFileSplitInfo:search'">
{{ $t('query') }}
</a-button>
<a-button type="primary" @click="searchReset" icon="reload" ghost>{{ $t('reset') }}</a-button>
</div>
</a-row>
</a-form>
</div>
<div class="table-operator">
<!--拆分已入库文本-->
<a-button icon="plus" type="primary" @click="handleModule" v-has="'split:sarFileSplitInfo:splitFile'">{{
$t('docTool.split.splitText')
}}
</a-button>
<!--导入拆分结果-->
<a-button icon="download" type="primary" ghost @click="handleExport" v-has="'split:sarFileSplitInfo:splitResult'">
{{ $t('docTool.split.importResults') }}
</a-button>
<!--批量删除-->
<a-button type="primary" icon="delete" ghost @click="batchDel" v-has="'split:sarFileSplitInfo:deleteBatch'">
{{ $t('batchDelete') }}
</a-button>
<!--更改权限-->
<a-button type="primary" icon="sync" ghost @click="handleChangePermission" v-has="'split:sarFileSplitInfo:changePermissions'">
{{ $t('docTool.split.changePermissions') }}
</a-button>
</div>
<div>
<j-table
:columns="columns"
:dataSource="dataSource"
:can-drag="true"
rowKey="id"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:pagination="ipagination"
:scroll="{x: '100%', y: yScrollHeight}"
:loading="loading"
:operation-list="operationList"
@change="handleTableChange"
@operationClick="operationClick">
<!--跳转标准详情-->
<template v-slot:detail="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text can-click-table-text" v-if="text || text === 0" @click="toDetailPage(record)">{{ text }}</div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</j-table>
</div>
<!--拆分已入库文本弹框-->
<split-text ref="splitText" @ok="modalFormOk" />
<!--导入拆分结果-->
<import-split-result-modal ref="importResultModal" @ok="modalFormOk" />
<!--更改权限-->
<user-select-modal ref="userSelectModal" check-required @change="continueChangePermission" />
</a-card>
</template>
<script>
import '../../../assets/less/common.less'
import JTable from '../../../components/jero/JTable.vue'
import SplitText from './modules/SplitText.vue'
import { JeroListMixin } from '@/mixins/JeroListMixin'
import { releaseSplitStandard, changePermission, withdraw } from '@api/documentToolApi'
import ImportSplitResultModal from './modules/ImportSplitResultModal.vue'
import UserSelectModal from '../../../components/selection/UserSelectModal.vue'
import { SplitStatus, StandardSource } from '@/enums/commonEnums'
import { mapGetters } from 'vuex'
export default {
name: 'DocumentSplit',
components: { JTable, SplitText, ImportSplitResultModal, UserSelectModal },
mixins: [JeroListMixin],
computed: {
...mapGetters(['userInfo']),
administrators () {
const adminRoleList = window._CONFIG.adminRoleCode.split(',')
const userRoleList = this.userInfo.roleCode.split(',')
return userRoleList.some(tt => adminRoleList.includes(tt))
}
},
data () {
return {
operationList: [
// 发布
{
text: this.$t('release'),
clickEvent: 'handleRelease',
has: 'split:sarFileSplitInfo:release',
show: (record) => {
// 创建人(更改权限更改的是这个字段)是当前登录人,且状态为编辑中可以发布
return record.createBy === this.userInfo.id && record.splitStatus === SplitStatus.EDITING.value
}
},
// 撤回
{
text: this.$t('withdraw'),
clickEvent: 'handleWithdraw',
has: 'split:sarFileSplitInfo:withdraw',
show: (record) => {
// 创建人(更改权限更改的是这个字段)是当前登录人,且状态为已发布可以撤回
return record.createBy === this.userInfo.id && record.splitStatus === SplitStatus.RELEASED.value
}
},
// 查看
{
text: this.$t('view'),
clickEvent: 'handleDetail',
has: 'split:sarFileSplitItems:page'
},
// 编辑
{
text: this.$t('edit'),
clickEvent: 'handleEdit',
has: 'split:sarFileSplitItems:edit',
show: (record) => {
// 创建人(更改权限更改的是这个字段)是当前登录人,且状态为编辑中可以编辑
return record.createBy === this.userInfo.id && record.splitStatus === SplitStatus.EDITING.value
}
},
// 删除
{
text: this.$t('delete'),
clickEvent: 'handleDelete',
has: 'split:sarFileSplitInfo:delete',
show: (record) => {
// 创建人(更改权限更改的是这个字段)是当前登录人或用户为管理员,且状态为编辑中可以删除
return (record.createBy === this.userInfo.id || this.administrators)
}
}
],
columns: [
// 标准号
{
dataIndex: 'serialNumber',
title: this.$t('standardSelect.standardNumber'),
width: 150,
scopedSlots: { customRender: 'detail' }
},
// 标准名称
{
dataIndex: 'title',
title: this.$t('standardSelect.standardName'),
width: 150,
scopedSlots: { customRender: 'detail' }
},
// 文件状态
{
dataIndex: 'fileType_dictText',
title: this.$t('docTool.split.fileStatus'),
width: 150
},
// 文件名称
{
dataIndex: 'fileName',
title: this.$t('fileName'),
width: 180
},
// 拆分状态
{
dataIndex: 'splitStatus_dictText',
title: this.$t('docTool.split.splitState'),
width: 150
},
// 编辑人
{
dataIndex: 'author',
title: this.$t('docTool.split.editor'),
width: 150
},
// 拆分时间
{
dataIndex: 'createTime',
title: this.$t('docTool.split.splitTime'),
width: 150
},
// 拆分结果
{
dataIndex: 'splitResult',
title: this.$t('docTool.split.splitResult'),
sorter: true,
width: 150
},
// 操作
{
title: this.$t('operation'),
fixed: 'right',
width: 150,
scopedSlots: { customRender: 'action' }
}
],
url: {
list: '/laws/DocumentTool/documentSplit/queryByPage', // 表格数据
deleteBatch: '/laws/DocumentTool/documentSplit/deleteBatch', // 批量删除
delete: '/laws/DocumentTool/documentSplit/delete'
}
}
},
methods: {
// 拆分已入库文本按钮点击
handleModule () {
this.$refs.splitText.open()
},
// 导入拆分结果
handleExport () {
this.$refs.importResultModal.open()
},
/**
* 发布
*/
handleRelease ({ id }) {
this.$confirm({
title: this.$t('confirmRelease'),
content: this.$t('confirmReleaseContent'),
onOk: () => {
this.loading = true
releaseSplitStandard({ id }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.modalFormOk()
} else {
this.$message.warn(res.message)
}
}).finally(() => {
this.loading = false
})
}
})
},
/**
* 撤回
* @param id
*/
handleWithdraw ({ id }) {
this.$confirm({
title: this.$t('confirmWithdraw'),
content: this.$t('confirmWithdrawContent'),
onOk: () => {
this.loading = true
withdraw({ id }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.modalFormOk()
} else {
this.$message.warn(res.message)
}
}).finally(() => {
this.loading = false
})
}
})
},
// 编辑
handleEdit (record) {
this.$router.push({
path: '/docTool/DocSplitDetail',
query: {
infoId: record.id,
infoNumber: record.serialNumber,
createBy: record.createBy,
standardName: record.title
}
})
},
/**
* 表格操作列的查看
* @param id
*/
handleDetail ({ id }) {
this.$openPageNewSheet({
path: '/docTool/StandardSplitSheet',
query: {
id
}
})
},
splitTextVisible (val, num) {
if (num === 1) {
this.$refs.tableData.$emit('searchGetData')
}
},
importResultVisible (val, num) {
if (num === 1) {
this.$refs.tableData.$emit('searchGetData')
}
},
/**
* 更改权限
*/
handleChangePermission () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectAtLeastOne'))
return
}
this.$refs.userSelectModal.open()
},
continueChangePermission (id) {
this.loading = true
const params = {
ids: this.selectedRowKeys.join(','),
createBy: id
}
changePermission(params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.modalFormOk()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
/**
* 跳转标准详情
* @param record
*/
toDetailPage ({ standardId, splitStandardSource }) {
let path
// 根据来源跳转不同的详情页
switch (splitStandardSource) {
// 国内
case StandardSource.DOMESTIC.value:
path = '/standardRegulationLibrary/DomesticStandardDetail'
break
// 海外
case StandardSource.OVERSEAS.value:
path = '/standardRegulationLibrary/OverseasStandardDetail'
break
// 企标
case StandardSource.ENTERPRISE.value:
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
break
default:
break
}
if (!path) {
return
}
const query = {
id: standardId
}
this.$openPageNewSheet({
path, query
})
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
</style>
@@ -0,0 +1,677 @@
<template>
<internal-detail-page :title="pageTitle" :content-padding="0">
<div class="page-left-right-layout">
<!--树部分-->
<div class="page-left-box">
<a-input-search class="tree-search" v-model="searchValue" :placeholder="$t('nodeQuickLookup')" @search="onSearch" />
<div class="page-tree-box page-tree-box-has-search">
<a-spin :spinning="treeLoading">
<a-tree :show-line="true"
:show-icon="true"
:tree-data="treeData"
:selected-keys="selectedKeys"
:expanded-keys.sync="expandedKeys"
:replaceFields="{key: 'id'}">
<template #title="record">
<div class="tree-operate-node"
@mouseenter="handleTreeMouseover(record.id)"
@mouseleave="handleTreeMouseout(record.id)"
@click="handleSelectTreeNode(record)">
<a-icon class="parent-node-icon"
type="folder"
v-if="record.dataRef.children && record.dataRef.children.length > 0" />
<a-tooltip>
<template #title>
{{ record.name }}{{ record.itemName }}
</template>
<span class="tree-node-custom-title">{{ record.name }} {{ record.itemName }}</span>
</a-tooltip>
<div class="tree-operate-node-btn-box"
:class="{'tree-operate-node-btn-box-hide': record.id !== mouseInNodeKey}">
<!--新增-->
<a-button icon="plus"
class="tree-operate-node-btn"
@click.stop="orgAdd(record)"
v-has="'split:sarFileSplitMenu:add'"></a-button>
<!--编辑-->
<a-button class="tree-operate-node-btn"
@click.stop="orgEdit(record)"
v-if="record.pid"
v-has="'split:sarFileSplitMenu:update'">
<i class="iconfont icon-bianji" />
</a-button>
<!--删除根节点不能删除-->
<a-button class="tree-operate-node-btn"
v-if="record.pid"
v-has="'split:sarFileSplitMenu:delete'"
@click.stop="orgDelete(record)">
<i class="iconfont icon-shanchu_" />
</a-button>
<!--节点上移是否可以上移在树形数据中应该有字段标识-->
<a-button icon="up"
v-if="record.pid"
class="tree-operate-node-btn"
@click.stop="orgMoveUp(record)"
v-has="'split:sarFileSplitMenu:moveUp'">
</a-button>
<!--节点下移是否可以下移在树形数据中应该有字段标识-->
<a-button icon="down"
v-if="record.pid"
class="tree-operate-node-btn"
@click.stop="orgmoveDown(record)"
v-has="'split:sarFileSplitMenu:moveDown'">
</a-button>
</div>
</div>
</template>
</a-tree>
</a-spin>
</div>
</div>
<!--表格部分-->
<div class="page-right-box">
<div class="table-page-search-wrapper">
<search ref="searchRef"
:url="url"
:get-query-condition-func="getQueryConditionFunc"
search-has="split:sarFileSplitItems:search"
@search="searchQuery"
@reset="searchReset" />
</div>
<!--操作按钮-->
<div class="table-operator">
<!--新增-->
<!--<a-button icon="plus" type="primary" @click="handleAdd" v-has="'split:sarFileSplitItems:add'">{{ $t('newlyAdded') }}</a-button>-->
<!--导入-->
<span @click="handleImport">
<a-button type="primary" icon="download" ghost v-if="!menuId">{{ $t('import') }}</a-button>
<a-upload v-if="menuId"
name="file"
:showUploadList="false"
:multiple="false"
:headers="tokenHeader"
:action="importExcelUrl"
v-has="'split:sarFileSplitItems:import'"
:data="uploadData"
@change="handleImportExcel">
<a-button type="primary" icon="download" ghost>{{ $t('import') }}</a-button>
</a-upload>
</span>
<!--导出-->
<a-button icon="upload"
type="primary"
ghost
v-has="'split:sarFileSplitItems:export'"
@click="handleExportXls">
{{ $t('export') }}
</a-button>
<!--批量删除-->
<a-button type="primary" icon="delete" ghost @click="batchDel" v-has="'split:sarFileSplitItems:batchDelete'">
{{ $t('batchDelete') }}
</a-button>
<!--模板下载-->
<a-button type="primary"
icon="download"
ghost
v-has="'split:sarFileSplitItems:template'"
@click="downTemplate($t('docTool.split.documentSplitTemplate'), '.zip')">
{{ $t('templateDownload') }}
</a-button>
<!--批量复制-->
<a-button type="primary" ghost @click="handleBatchCopy" v-has="'split:sarFileSplitItems:batchCopy'">
{{ $t('docTool.split.batchCopy') }}
</a-button>
<!--批量编辑-->
<a-button type="primary" ghost @click="handleBatchEdit" v-has="'split:sarFileSplitItems:batchUpdate'">
{{ $t('docTool.split.batchEdit') }}
</a-button>
<!--合并条款-->
<a-button type="primary" ghost @click="handleMerge" v-has="'split:sarFileSplitItems:merge'">
{{ $t('docTool.split.mergerClause') }}
</a-button>
</div>
<div>
<j-table
v-if="columns && columns.length > 0"
:columns="columns"
:dataSource="dataSource"
:can-drag="true"
rowKey="id"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:pagination="ipagination"
:scroll="{x: '100%', y: yScrollHeight}"
:operation-list="operationList"
:loading="loading"
@change="tableOnChange"
@operationClick="operationClick">
<!-- 详情-->
<template slot="detailClick" slot-scope="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a class="table-text textName" @click="detailClick(record)" v-if="text || text === 0">{{ text }}</a>
<div class="table-text" v-else>{{ global.emptyLine }}</div>
</a-tooltip>
</template>
<!--条文内容-->
<template slot="item_content" slot-scope="{text, record}">
<div class="table-text" v-if="text || text === 0" @click="showContent('item_content', text,record)">
{{ text && text !== 'null' ? text.replace(/<.*?>/ig, ' ') : '' }}
</div>
<div class="table-text" v-else>{{ global.emptyLine }}</div>
</template>
<!--条文解读-->
<template slot="interpretation_articles" slot-scope="{text, record}">
<div class="table-text" v-if="text || text === 0" @click="showContent('interpretation_articles', text,record)">
{{ text && text !== 'null' ? text.replace(/<.*?>/ig, ' ') : '' }}
</div>
<div class="table-text" v-else>{{ global.emptyLine }}</div>
</template>
</j-table>
</div>
</div>
</div>
<!--条文内容条文解读-->
<split-clause-detail ref="splitClauseDetail" />
<!--添加节点-->
<split-detail-node-add ref="splitDetailNodeAdd" @ok="nodeAddOk" />
<!--批量编辑-->
<split-detail-batch-edit-drawer ref="batchEditDrawer" @ok="modalFormOk" />
<!--条款添加-->
<split-add-form ref="modalForm" :flag="'4'" :infoId="infoId" :menuId="menuId" :itemId="itemId" @ok="modalFormOk" />
</internal-detail-page>
</template>
<script>
import InternalDetailPage from '../../../components/InternalDetailPage.vue'
import JTable from '../../../components/jero/JTable.vue'
// 条款添加
import SplitAddForm from './modules/SplitAddForm.vue'
// 条款内容弹框
import SplitClauseDetail from './modules/SplitClauseDetail.vue'
// 添加左侧树节点弹框
import SplitDetailNodeAdd from './modules/SplitDetailNodeAdd.vue'
import { mapGetters } from 'vuex'
import { ConfigurableTableMixin } from '../../../mixins/ConfigurableTableMixin.js'
import {
getDocSplitTableHeader,
getDocSplitSearch,
deleteClauseTreeNode,
moveUpOrDownClauseTreeNode,
batchCopyClause,
merageClause,
getClauseTreeData,
copyClause
} from '../../../api/documentToolApi.js'
import Search from '../../../components/customField/Search.vue'
import { isHasPermission } from '../../../utils/hasPermission.js'
import SplitDetailBatchEditDrawer from './modules/SplitDetailBatchEditDrawer.vue'
import { downFile, downloadFile, postAction } from '../../../api/manage'
export default {
name: 'DocumentSplitDetail',
components: {
InternalDetailPage,
JTable,
SplitAddForm,
SplitClauseDetail,
SplitDetailNodeAdd,
Search,
SplitDetailBatchEditDrawer
},
mixins: [ConfigurableTableMixin],
data () {
return {
data: [],
loading: false, // 提交的加载
treeLoading: false, // 树形区域的加载
confirmLoading: false,
nodeTreeItem: null, // 右键菜单
tmpStyle: '', // 右键菜单位置
infoId: '',
menuId: '',
itemId: '',
itemVal: {},
uploadMenuId: '',
deleteNode: false,
moveDownFlag: false,
moveUpFlag: false,
fullWidth: '',
parameter: {},
showAction: false,
flag: '4', // 查询要用到
operateSlotName: 'action',
getQueryConditionFunc: getDocSplitSearch,
getTableHeaderFunc: getDocSplitTableHeader,
treeData: [],
searchValue: '',
selectedKeys: [],
mouseInNodeKey: null, // 鼠标悬浮的节点key值
// 操作栏
operationList: [
// 编辑
{
text: this.$t('edit'),
clickEvent: 'handleEdit',
has: 'split:sarFileSplitItems:update'
},
// 复制
{
text: this.$t('copy'),
clickEvent: 'handleCopy',
has: 'split:sarFileSplitItems:copy'
},
// 删除
{
text: this.$t('delete'),
clickEvent: 'handleDelete',
has: 'split:sarFileSplitItems:delete'
}
],
url: {
list: '/laws/DocumentTool/documentSplit/queryDocumentSplitDetail',
importUrl: '/split/sarFileSplitItems/importSplitItems', // 导入
exportXlsUrl: '/split/sarFileSplitItems/exportSplitInfoZip', // 导出
delete: '/laws/DocumentTool/documentSplit/deleteDocumentSplitDetail', // 删除
deleteBatch: '/laws/DocumentTool/documentSplit/deleteBatchDocumentSplitDetail', // 批量删除
downTemplateUrl: '/laws/DocumentTool/documentSplit/downloadImportTemplate' // 模板下载
},
disabledMixinsCreate: true,
tableSlotField: ['item_content', 'interpretation_articles'],
uploadData: {},
isBackTitle: true,
expandedKeys: []
}
},
computed: {
// 页面标题是标准名称
pageTitle () {
return this.$route.query.infoNumber + ' ' + this.$route.query.standardName
},
yScrollHeight () {
/**
* 前几个使用变量是为了配合iframe嵌套,算出页面显示范围的高度
* 48px:分页器的高度
* 48pxa-card的padding(上24+下24
* 54px:表头行的高度
*/
return `calc(100vh - ${this.defaultHeight['user-info-height']} - ${this.defaultHeight['breadcrumb-height']} - ${this.defaultHeight.oneLineOperationHeight} - 48px - 48px - 54px - ${this.isBackTitle ? '56px' : '0px'})`
},
...mapGetters(['userInfo']),
administrators () {
const adminRoleList = window._CONFIG.adminRoleCode.split(',')
const userRoleList = this.userInfo.roleCode.split(',')
return userRoleList.some(tt => adminRoleList.includes(tt))
}
},
mounted () {
this.operateColumn.scopedSlots.customRender = 'action'
this.showAction = this.$route.query.createBy === this.userInfo.id || this.administrators
this.infoId = this.$route.query.infoId
this.filters = {
menu_id: this.menuId,
info_id: this.infoId
}
this.getTableHeader()
this.loadData()
// 过滤没有权限的按钮
if (this.needFilterTableBtn) {
this.operationList = this.operationList.filter(item => !item.has || isHasPermission(item.has))
}
this.infoId = this.$route.query.infoId
this.loadMenuData()
},
methods: {
/**
* 树节点鼠标移入
* @param key
*/
handleTreeMouseover (key) {
this.mouseInNodeKey = key
},
/**
* 树节点鼠标移出
* @param key
*/
handleTreeMouseout (key) {
if (this.mouseInNodeKey === key) {
this.mouseInNodeKey = null
}
},
/**
* 选中树节点
* @param dataRef
*/
handleSelectTreeNode ({ dataRef }) {
this.selectedKeys = [dataRef.id]
this.menuId = this.selectedKeys[0]
this.uploadData = {
menuId: this.menuId,
infoId: this.infoId
}
this.filters.info_id = this.infoId
this.filters.menu_id = this.menuId
this.loadData()
},
/**
* 显示条款内容、条文解读弹框
* @param fieldName
* @param val
*/
showContent (fieldName, val) {
const title = (this.columns.find(tt => tt.dbFieldName === fieldName) || {}).dbFieldTxt
if (title) {
this.$refs.splitClauseDetail.title = title
}
this.$refs.splitClauseDetail.open(val)
},
// 获取左侧目录数据
loadMenuData () {
const params = {
infoId: this.infoId,
name: this.searchValue
}
this.treeLoading = true
getClauseTreeData(params).then(res => {
if (res.success) {
this.treeData = res.result || []
this.expandedKeys = this.treeData.length > 0 ? [this.treeData[0].id] : []
}
}).finally(() => {
this.treeLoading = false
})
},
// 用于点击空白处隐藏增删改菜单
clearMenu () {
this.nodeTreeItem = null
},
// 树形新增
orgAdd (record) {
console.log(record)
this.$refs.splitDetailNodeAdd.add(record.dataRef, this.infoId)
},
// 树形修改
orgEdit (record) {
this.$refs.splitDetailNodeAdd.edit(record.dataRef, this.infoId)
},
// 添加节点完成后的回调
nodeAddOk (expandId) {
if (expandId && expandId.length > 0) {
this.onExpand(expandId)
}
this.loadMenuData()
this.loadData()
},
// 树形删除
orgDelete (record) {
this.$confirm({
content: this.$t('confirmDeleteNodes'),
onOk:
async () => {
deleteClauseTreeNode({ id: record.dataRef.id }).then(res => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.loadMenuData()
this.loadData()
} else {
this.$message.warning(this.$t('operationFailed'))
}
})
}
})
},
// 树上移
orgMoveUp (record) {
const query = {
moveFlag: 'up',
infoId: this.infoId,
sarFileSplitMenu: {
id: record.dataRef.id,
pId: record.dataRef.pid,
displaySeq: record.dataRef.displaySeq
}
}
moveUpOrDownClauseTreeNode(query).then(res => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.loadMenuData()
this.loadData()
} else {
this.$message.warning(this.$t('operationFailed'))
}
})
},
// 树下移
orgmoveDown (record) {
const query = {
moveFlag: 'down',
infoId: this.infoId,
sarFileSplitMenu: {
id: record.dataRef.id,
pId: record.dataRef.pid,
displaySeq: record.dataRef.displaySeq
}
}
moveUpOrDownClauseTreeNode(query).then(res => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.loadMenuData()
this.loadData()
} else {
this.$message.warning(this.$t('operationFailed'))
}
})
},
handleCopy ({ id }) {
this.$confirm({
title: this.$t('confirmReplication'),
content: '',
onOk: () => {
this.loading = true
copyClause({ id }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loadData()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
}
})
},
/**
* 批量复制
*/
handleBatchCopy () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectLeastOne'))
return
}
this.$confirm({
title: this.$t('confirmReplication'),
content: '',
onOk: () => {
batchCopyClause({ ids: this.selectedRowKeys.join(',') }).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.onClearSelected()
this.loadMenuData()
this.loadData()
} else {
this.$message.warning(this.$t('operationFailed'))
}
})
}
})
},
// 新增
handleAdd () {
if (!this.menuId) {
this.$message.warning(this.$t('docTool.split.selectDirectoryTerms'))
return
}
if (this.menuId) {
this.formTitle = this.$t('add')
this.itemId = ''
this.$refs.modalForm.add()
}
},
// 合并条款
handleMerge () {
if (this.selectedRowKeys && this.selectedRowKeys.length < 2) {
this.$message.warning(this.$t('leastTwo'))
return
}
const ids = this.selectedRowKeys.join(',')
this.$confirm({
title: this.$t('docTool.split.mergeTerms'),
content: '',
onOk: () => {
this.loading = true
merageClause({ ids: ids }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loading = false
this.onClearSelected()
this.loadMenuData()
this.loadData()
this.selectedRowKeys = []
} else {
this.$message.warning(res.message)
this.loading = false
}
})
}
})
},
/**
* 批量编辑
*/
handleBatchEdit () {
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectLeastOne'))
return
}
this.$refs.batchEditDrawer.open(this.selectedRowKeys.join(','))
},
onSearch () {
this.loadMenuData()
},
/**
* 删除
* @param id
*/
handleDelete (id) {
if (!this.url.delete) {
this.$message.warning('请设置url.delete属性!')
return
}
const that = this
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('areYouSure'),
onOk: () => {
postAction(that.url.delete, { id: id }).then((res) => {
if (res.success) {
that.$message.success(res.message)
// 判断当前删除的数据是否是最后一页的最后一条数据,如果是的话页码减一
if (that.ipagination.current > 1 && ((that.ipagination.current - 1) * that.ipagination.pageSize) + 1 === that.ipagination.total) {
that.ipagination.current -= 1
}
that.loadData()
} else {
that.$message.warning(res.message)
}
})
}
})
},
/**
* 导出
*/
handleExportXls () {
const fileName = this.$route.query.infoNumber + ' ' + this.$route.query.standardName
const fileSuffix = '.zip'
const param = {}
if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
param.idList = this.selectedRowKeys.join(',')
}
param.parameter = this.getQueryParams()
param.exportName = fileName + fileSuffix
// 加一个大的提示
const modalLoading = this.$info({
title: this.$t('tips'),
content: <span>{this.$t('exportTip')}
<a-spin size="small" /></span>,
keyboard: false
})
// 把知道了这个按钮去掉
this.$nextTick(() => {
document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none'
})
downFile(this.url.exportXlsUrl, param).then((data) => {
if (!data) {
this.$message.warning(this.$t('fileDownloadFail'))
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName + fileSuffix)
} else {
const url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' }))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + fileSuffix)
document.body.appendChild(link)
link.click()
document.body.removeChild(link) // 下载完成移除元素
window.URL.revokeObjectURL(url) // 释放掉blob对象
}
}).finally(() => {
// 销毁这个提示
modalLoading.destroy()
})
},
handleImport () {
if (!this.menuId) {
this.$message.warning(this.$t('docTool.split.selectDirectoryTerms'))
}
},
// 下载导入模板
downTemplate (fileName, fileSuffix = '.xls') {
if (!fileName || typeof fileName !== 'string') {
fileName = '模板文件'
}
downloadFile(this.url.downTemplateUrl, fileName + fileSuffix, { infoId: this.$route.query.infoId })
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
.page-left-right-layout {
height: 100%;
width: 100%;
display: flex;
}
/deep/ .ant-tree li span.ant-tree-switcher.ant-tree-switcher-noop {
color: #1D2129;
}
.parent-node-icon {
color: #1D2129;
font-size: 16px;
margin-right: 10px;
}
</style>
@@ -0,0 +1,34 @@
<template>
<div class="split-content">
<standard-resolution-sheet ref="resolutionSheet" :horizontal-overstep="true"/>
</div>
</template>
<script>
import '@assets/less/common.less'
import StandardResolutionSheet from '../../../components/StandardResolutionSheet.vue'
export default {
name: 'StandardSplitSheet',
components: { StandardResolutionSheet },
mounted () {
this.$nextTick(() => {
this.$refs.resolutionSheet.queryParams = { id: this.$route.query.id }
this.$refs.resolutionSheet.initClauseFieldList()
this.$refs.resolutionSheet.initDetailInfo()
})
}
}
</script>
<style scoped lang="less">
.split-content {
height: 100vh;
padding: 20px 0 20px 20px;
background-color: #eff1f4;
}
/deep/ .clause-label-change {
border-radius: 0;
}
</style>
@@ -0,0 +1,220 @@
<template>
<j-modal
:title="$t('docTool.split.importResults')"
:width="width"
:visible="visible"
switchFullscreen
:maskClosable="false"
:confirmLoading="confirmLoading"
:ok-text="$t('docTool.split.split')"
:cancel-text="$t('cancel')"
@ok="handleOk"
@cancel="handleCancel">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<!--标准号-->
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('standardSelect.standardNumber')">
<standard-selection :placeholder="$t('pleaseSelect') + $t('standardSelect.standardNumber')"
type="radio"
@nameChange="handleStandardNumberChange"
:select-only="true"
:can-selected-source="[StandardSource.DOMESTIC.value, StandardSource.ENTERPRISE.value]"
class="readonly-input"
v-decorator="['serialNumber', validatorRules.serialNumber]" />
</a-form-item>
<!--标准名称-->
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('standardSelect.standardName')">
<a-input :placeholder="$t('pleaseEnter') + $t('standardSelect.standardName')"
:maxLength="50"
disabled
v-decorator="['title', validatorRules.title]" />
</a-form-item>
<!--文本状态-->
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('docTool.comparison.textStatus')">
<j-dict-select-tag
class="box-input"
v-decorator="['fileType', validatorRules.fileType]"
:placeholder="$t('pleaseSelect') + $t('docTool.comparison.textStatus')"
:type="'select'"
:triggerChange="false"
:dictCode="'process_manuscript'" />
</a-form-item>
<!--拆分结果文件-->
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('docTool.split.splitResultFile')">
<div class="result-file-box">
<j-upload v-decorator="['splitFileId', validatorRules.splitFileId]"
return-id
:number="1"
class="line-btn"
file-type="zip" />
<a-button class="line-btn" @click="downTemplate($t('docTool.split.documentSplitTemplate'), '.zip')">
{{ $t('templateDownload') }}
</a-button>
</div>
</a-form-item>
</a-form>
<div class="tip">
<a-icon type="exclamation-circle" class="tip-icon" />
<span>{{ $t('docTool.split.splitImportResultTip') }}</span>
<span class="tip-click" @click="showTips">{{ $t('docTool.split.clickToView') }}</span>
</div>
</a-spin>
</j-modal>
</template>
<script>
import StandardSelection from '../../../../components/selection/StandardSelection.vue'
import { importSplitResult } from '@api/documentToolApi'
import { getFileInfo } from '@api/api'
import { downloadFile } from '@api/manage'
import { StandardSource } from '../../../../enums/commonEnums'
export default {
name: 'ImportSplitResultModal',
components: { StandardSelection },
data () {
return {
StandardSource,
width: 600,
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
validatorRules: {
// 标准号
serialNumber: {
rules: [{ required: true, message: this.$t('pleaseSelect') + this.$t('standardSelect.standardNumber') }],
validateTrigger: 'change'
},
// 标准名称
title: {
rules: [{ required: false, message: this.$t('pleaseEnter') + this.$t('standardSelect.standardName') }],
validateTrigger: 'blur'
},
// 文本状态
fileType: {
rules: [{ required: true, message: this.$t('pleaseSelect') + this.$t('docTool.comparison.textStatus') }],
validateTrigger: 'change'
},
// 拆分结果文件
splitFileId: {
rules: [{ required: true, message: this.$t('uploadFile.pleaseUpload') + this.$t('docTool.split.splitResultFile') }],
validateTrigger: 'change'
}
},
downTemplateUrl: '/laws/DocumentTool/documentSplit/downloadImportTemplate' // 模板下载
}
},
methods: {
open () {
this.visible = true
},
handleOk () {
this.form.validateFields(async (errors, values) => {
if (!errors) {
this.confirmLoading = true
const formData = values
const fileInfo = await getFileInfo({ id: formData.splitFileId })
if (fileInfo.success) {
formData.fileName = fileInfo.result.fileName
}
importSplitResult(formData).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.form.resetFields()
},
showTips () {
this.$info({
closeable: true, // 展示右上角关闭
cancelText: this.$t('docTool.split.gotIt'),
title: this.$t('docTool.split.formatRequirement'),
content: (h) => {
const contentArr = [this.$t('docTool.split.formatRequirementContentOne'), this.$t('docTool.split.formatRequirementContentTwo'), this.$t('docTool.split.formatRequirementContentThree'), this.$t('docTool.split.formatRequirementContentFour')]
const contentDomArr = []
for (const content of contentArr) {
console.log(content)
contentDomArr.push(h('div', {}, content))
}
return contentDomArr
}
})
},
handleStandardNumberChange (name) {
this.form.setFieldsValue({ title: name })
},
// 下载导入模板
downTemplate (fileName, fileSuffix = '.xls') {
if (!fileName || typeof fileName !== 'string') {
fileName = '模板文件'
}
downloadFile(this.downTemplateUrl, fileName + fileSuffix)
}
}
}
</script>
<style scoped lang="less">
.line-btn {
display: inline-block;
}
.line-btn:last-child {
margin-left: 12px;
}
.tip {
margin: 0 42px;
&-icon {
color: @primary-color;
margin-right: 8px;
}
&-click {
color: @primary-color;
cursor: pointer;
}
}
.result-file-box {
display: flex;
align-items: baseline;
}
.result-file-box > div {
flex: 1;
width: 0;
}
.readonly-input {
/deep/ .ant-input[disabled] {
background-color: white;
color: rgba(0, 0, 0, 0.65);
}
}
</style>
@@ -0,0 +1,960 @@
<template>
<a-drawer
:title="title"
placement="right"
:visible="visible"
@close="handleCancel"
width="900"
class="custom-drawer-style">
<div class="custom-drawer-style-scroll">
<a-spin :spinning="loading">
<!--表单部分-->
<a-form-model :model="formInline" v-if="isFormInline" class="form-add" :rules="rules" ref="ruleForm">
<div class="form-flex-box">
<template v-for="(item, index) in dataList">
<div :key="index"
class="box-title-text form-flex-item"
v-if="!hiddenFieldList.includes(item.dbFieldName)">
<div class="title-text">
<span class="required" v-if="item.fieldMustInput === '1'">*</span>
<span class="title-text-text" :title="item.dbFieldTxt">{{ item.dbFieldTxt }}</span>
</div>
<a-form-model-item class="item-model" :prop="item.dbFieldName">
<!--条文解读-->
<template v-if="item.dbFieldName === 'interpretation_articles'">
<div class="unscramble-item"
v-for="(unscramble, unscrambleIndex) in interpretationArticlesList"
:key="unscrambleIndex + 'interpretation_articles'">
<div class="unscramble-item-content">
<!--文本-->
<a-input type="textarea" :maxLength="500" v-model="unscramble.itemContent" />
</div>
<div class="unscramble-item-operate">
<a-button @click="handleAddUnscramble(unscrambleIndex)" icon='plus'></a-button>
<a-button @click="handleDeleteUnscramble(unscrambleIndex, unscramble)" icon='minus'
:disabled="interpretationArticlesList.length === 1 && unscrambleIndex === interpretationArticlesList.length - 1">
</a-button>
</div>
</div>
</template>
<!-- 1, 日期单选 -->
<a-date-picker v-else-if="item.fieldShowType === FieldType.DATE_SINGLE.value"
class="box-input"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:getCalendarContainer="(trigger) => trigger.parentNode"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
@change="updateDom"
style="width: 100%" />
<!-- 2, 单选用户 -->
<user-selection v-else-if="item.fieldShowType === FieldType.USER_SINGLE.value"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
v-model="formInline[item.dbFieldName]"
:filedName="item.dbFieldName"
@nameChange="userSelectNameChange"
:nameStr="formInline[item.dbFieldName + '_dictText']"
type="radio"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)" />
<!-- 3, 单选组织机构 -->
<j-select-depart v-else-if="item.fieldShowType === FieldType.ORGAN_SINGLE.value"
v-model="formInline[item.dbFieldName]"
:backDepart="true" />
<!-- 4, 日期多选 -->
<j-multiple-date-picker v-else-if="item.fieldShowType === FieldType.DATE_MORE.value"
class="box-input"
v-model="formInline[item.dbFieldName]"
:fieldName="item.dbFieldName"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)" />
<!-- 5, 多行文本 -->
<a-textarea v-else-if="item.fieldShowType === FieldType.TEXTAREA.value"
:placeholder="$t('pleaseEnter')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:maxLength="500"
v-model="formInline[item.dbFieldName]" :rows="4" />
<!-- 6, 标准多选 -->
<standard-selection v-else-if="item.fieldShowType === FieldType.STANDARD_MORE.value"
:source="flag"
:disabledSourceSearch="true"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
v-model="formInline[item.dbFieldName]" />
<!-- 7, 多选组织机构 -->
<j-select-depart v-else-if="item.fieldShowType === FieldType.ORGAN_MORE.value"
v-model="formInline[item.dbFieldName]"
:multi="true"
:backDepart="true"
:treeOpera="true" />
<!-- 8, 上传文件 -->
<a-button v-else-if="item.fieldShowType === FieldType.FILE_UP.value" type="primary" class="button-text"
@click="clickButtonToUpload(item)">
{{
(formInline[item.dbFieldName] === 'null' || formInline[item.dbFieldName] === ''
|| formInline[item.dbFieldName] === null || formInline[item.dbFieldName] === undefined)
? $t('uploadFile.clickUpload')
: $t('uploadFile.viewUploadedFiles')
}}
</a-button>
<!-- 9, 文本框 -->
<a-input v-else-if="item.fieldShowType === FieldType.TEXT_BOX.value"
class="box-input"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
v-model="formInline[item.dbFieldName]"
:maxLength="50"
:placeholder="(specialPlaceholder[item.dbFieldName] || $t('pleaseEnter')+item.dbFieldTxt) || + getPlaceholderSuffix(item)" />
<!-- 10, 下拉单选数据字典 -->
<j-dict-select-tag v-else-if="item.fieldShowType === FieldType.OPTION_SINGLE.value"
class="box-input"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:type="radioDictSelect.includes(item.dbFieldName)? 'radio' : 'select'"
:triggerChange="false"
@change="updateDom"
:dictCode="item.dictField" />
<!-- 11, 下拉多选数据字典 -->
<j-multi-select-tag v-else-if="item.fieldShowType === FieldType.OPTION_MORE.value"
class="box-input"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:type="'select'"
@change="updateDom"
:triggerChange="false"
:dictCode="item.dictField" />
<!-- 12, 树选择 -->
<s-tree-select
v-else-if="item.fieldShowType === FieldType.TREE.value"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:tree-data="optionsData[item.dbFieldName]"
tree-checkable
treeCheckStrictly
:label-in-value="false"
@change="updateDom"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)" />
<!-- 14, 年份选择 -->
<j-date v-else-if="item.fieldShowType === FieldType.YEAR_PICKER.value"
show-type="year"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
v-model="formInline[item.dbFieldName]"
:default-value="defaultValue[item.dbFieldName]"
date-format="YYYY"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)" />
<!-- 15, 树选择单选 -->
<s-tree-select v-else-if="item.fieldShowType === FieldType.TREE_SINGLE.value"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:tree-data="optionsData[item.dbFieldName]"
:label-in-value="false"
@change="updateDom"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)" />
<!--16树形弹框选择-->
<tree-selection v-else-if="item.fieldShowType === FieldType.TREE_MODAL_SELECT.value"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
v-model="formInline[item.dbFieldName]"
:filedName="item.dbFieldName"
type="checkbox"
:options-href="item.fieldHref"
:nameStr="formInline[item.dbFieldName + '_dictText']"
:dict-id="item.dictId"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:children-column-name="getTreeSelectionChildrenColumnName(item)"
:is-tag-library-restriction="item.dbFieldName === 'property_tag'"
:search-field-name="item.dbFieldName === 'property_tag' ? 'firstNodeName' : null" />
</a-form-model-item>
</div>
</template>
</div>
<upload-file ref="uploadFile" @change="uploadFileChange" :return-url="false"></upload-file>
</a-form-model>
<!--条款内容部分-->
<div class="header-text">
{{ $t('docTool.split.clauseContent') }}
<!--新增条款项-->
<a-button type="primary" icon="plus" ghost @click="handleAddClauseContent(-1)">{{ $t('newlyAdded') }}</a-button>
</div>
<template v-if="contentList && contentList.length > 0">
<div class="clause-item" v-for="(clause, index) in contentList" :key="index + 'clause'">
<div class="clause-item-content">
<!--文本-->
<a-input type="textarea"
:maxLength="500"
v-model="clause.itemContent"
v-if="clause.type === TypeOfClauseItem.TEXT.value" />
<!--图片-->
<viewer>
<img v-if="clause.type === TypeOfClauseItem.IMG.value"
:src="clause.thumbUrl?clause.thumbUrl:clause.itemContent"
alt="">
</viewer>
<!--表格-->
<div v-if="clause.type === TypeOfClauseItem.TABLE.value" v-html="clause.itemContent"></div>
</div>
<div class="clause-item-operate">
<a-button @click="handleAddClauseContent(index)">{{ $t('newlyAdded') }}</a-button>
<a-button v-if="clause.type === TypeOfClauseItem.TABLE.value" @click="splitEdit(index,clause)">{{
$t('edit')
}}
</a-button>
<a-button @click="handleDeleteClauseContent(index, clause)">{{ $t('delete') }}</a-button>
</div>
</div>
</template>
</a-spin>
</div>
<div class="custom-drawer-style-bottom-btn">
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button type="primary" class="footer-btn-save" @click="handleSave">{{ $t('preservation') }}</a-button>
</div>
<!-- 类型选择弹框 -->
<split-detail-add-type-check ref="splitDetailAddTypeCheck" @ok="handleAddOk" />
<!-- 编辑表格弹框 -->
<split-detail-add-table ref="splitDetailAddTable" @editOk="editTableOk" />
</a-drawer>
</template>
<script>
import UploadFile from '../../../../components/UploadFile.vue'
import StandardSelection from '../../../../components/selection/StandardSelection.vue'
// 类型选择弹框
import SplitDetailAddTypeCheck from './SplitDetailAddTypeCheck.vue'
import SplitDetailAddTable from './SplitDetailAddTable.vue'
import {
getDocSplitEditForm,
getDocSplitEditFormData,
addClause,
editClause,
getEditClauseContent,
getInterpretationArticlesList
} from '../../../../api/documentToolApi.js'
import UserSelection from '../../../../components/selection/UserSelection.vue'
import JMultipleDatePicker from '../../../../components/JMultipleDatePicker.vue'
import { ClauseUsableRange, FieldType, TypeOfClauseItem, YesOrNoEnum } from '../../../../enums/commonEnums'
import { getAction } from '../../../../api/manage'
import { getFormDictTreeList } from '../../../../api/api'
import STreeSelect from '../../../../components/STreeSelect'
import TreeSelection from '../../../../components/selection/TreeSelection'
export default {
name: 'SplitAddForm',
components: {
TreeSelection,
JMultipleDatePicker, UserSelection,
SplitDetailAddTable,
UploadFile,
StandardSelection,
SplitDetailAddTypeCheck,
STreeSelect
},
data () {
return {
FieldType,
TypeOfClauseItem,
hiddenFieldList: ['item_content'], // 隐藏的字段
radioDictSelect: ['is_long_effect'], // 使用radio样式的数据字典单选
disabledFieldList: [], // 禁用的字段
specialPlaceholder: {}, // 特殊的placeholder
optionsData: {}, // 通过接口获取的下拉数据
title: this.$t('newlyAdded'),
contentList: [], // 条款内容数据
visible: false,
addIndex: 0, // 新增条款的index
disabled: false,
formInline: {},
isFormInline: false,
dataList: [],
ruleList: [],
rules: {},
uploadName: '',
type: 'add',
loading: false,
submitFlag: false,
deleteIds: [],
itemVal: {},
contentTrees: '',
isTableEdit: false,
interpretationArticlesList: [] // 条文解读数据
}
},
props: {
flag: {
type: String,
default: ''
},
infoId: {
type: String,
default: ''
},
menuId: {
type: String,
default: ''
},
itemId: {
type: String,
default: ''
}
},
watch: {
// 是否长期有效,是的话新认证实施日期、已认证实施日期、新注册实施日期为多选,否则为单选
'formInline.is_long_effect': {
handler (value) {
const indexArr = []
// 新认证实施日期
indexArr[0] = this.dataList.findIndex(tt => tt.dbFieldName === 'new_cer_implement_date')
// 已认证实施日期
indexArr[1] = this.dataList.findIndex(tt => tt.dbFieldName === 'cer_implement_date')
// 新注册实施日期
indexArr[2] = this.dataList.findIndex(tt => tt.dbFieldName === 'new_register_implement_date')
if (value === YesOrNoEnum.YES.value) {
indexArr.forEach(index => {
this.$set(this.dataList[index], 'fieldShowType', FieldType.DATE_MORE.value)
})
} else if (value === YesOrNoEnum.NO.value) {
indexArr.forEach(index => {
this.$set(this.dataList[index], 'fieldShowType', FieldType.DATE_SINGLE.value)
})
}
this.$forceUpdate()
}
}
},
methods: {
/**
* 新增
*/
add () {
this.visible = true
this.formInline = {}
this.contentList = []
this.type = 'add'
this.getForm()
},
/**
* 编辑
* @param item
*/
edit (item) {
this.visible = true
this.type = 'edit'
this.itemVal = item
this.formInline = {}
this.getForm(() => {
this.loadFormInfo()
this.getClauseContentList()
this.initInterpretationArticlesList()
})
},
/**
* 获取表单字段
* @param callBack
*/
getForm (callBack) {
this.loading = true
const params = {
flag: this.flag,
type: this.type
}
getDocSplitEditForm(params).then((res) => {
if (res.success) {
this.dataList = res.result
this.ruleList = res.result
this.integrateData()
this.getOptionsData()
callBack && callBack()
}
}).finally(() => {
this.loading = false
})
},
/**
* 处理需要通过接口获取的下拉数据
*/
getOptionsData () {
const formList = Object.values(this.dataList).reduce((acc, cur) => acc.concat(cur), [])
formList.forEach(item => {
if (item.fieldShowType === FieldType.TREE.value || item.fieldShowType === FieldType.TREE_SINGLE.value) {
// 展示类型是树选择,且有接口的
if (item.fieldHref) {
getAction(item.fieldHref).then(res => {
if (res.success) {
this.$set(this.optionsData, item.dbFieldName, res.result)
}
})
} else if (item.dictId) {
getFormDictTreeList({ dictId: item.dictId }).then(res => {
if (res.success) {
this.$set(this.optionsData, item.dbFieldName, res.result)
}
})
}
}
})
},
/**
* 设置表单校验信息
*/
integrateData () {
this.isFormInline = false
const ruleList = JSON.parse(JSON.stringify(this.ruleList))
console.log(this.ruleList)
const rules = {}
ruleList.forEach((res, index) => {
const rule = []
if (res.fieldMustInput === '1') {
if ([FieldType.TEXT_BOX.value, FieldType.TEXTAREA.value].includes(res.fieldShowType)) {
rule.push({
required: true,
message: res.dbFieldTxt + this.$t('cannotEmpty'),
trigger: 'blur'
})
} else {
rule.push({
required: true,
message: res.dbFieldTxt + this.$t('cannotEmpty'),
trigger: 'change'
})
}
} else {
if ([FieldType.TEXT_BOX.value, FieldType.TEXTAREA.value].includes(res.fieldShowType)) {
rule.push({
required: false,
message: res.dbFieldTxt + this.$t('cannotEmpty'),
trigger: 'blur'
})
} else {
rule.push({
required: false,
message: res.dbFieldTxt + this.$t('cannotEmpty'),
trigger: 'change'
})
}
}
if (rule.length > 0) {
rules[res.dbFieldName] = rule
}
})
this.rules = rules
this.isFormInline = true
this.loading = false
},
/**
* 获取表单信息
*/
loadFormInfo () {
getDocSplitEditFormData({ id: this.itemVal.id }).then(res => {
if (res.success) {
// 处理树结构多选回显
const formList = Object.values(this.dataList).reduce((acc, cur) => acc.concat(cur), [])
const treeMultipleField = formList.filter(tt => tt.fieldShowType === FieldType.TREE.value)
const data = res.result || {}
treeMultipleField.forEach(field => {
const valueArr = []
if (data[field.dbFieldName]) {
const values = data[field.dbFieldName].split(',')
for (let i = 0; i < values.length; i++) {
valueArr[i] = {
value: values[i]
}
}
data[field.dbFieldName] = valueArr
} else {
data[field.dbFieldName] = undefined
}
})
const dictField = formList.filter(tt => tt.fieldShowType === FieldType.OPTION_SINGLE.value)
dictField.forEach(field => {
data[field.dbFieldName] = data[field.dbFieldName] || null
})
// 是否长期有效默认选否
if (!data.is_long_effect) {
data.is_long_effect = YesOrNoEnum.NO.value
}
// 处理条文解读
if (!data.interpretation_articles) {
data.interpretation_articles = ''
}
this.formInline = Object.assign({}, { ...data })
}
})
},
/**
* 获取条款内容列表
*/
getClauseContentList () {
this.loading = true
getEditClauseContent({ id: this.itemVal.id }).then(res => {
if (res.success) {
this.contentList = [...res.result]
this.contentList.forEach(item => {
const imgData = item.itemContent.split('fileName=')[1]
item.thumbUrl = window._CONFIG.domianURL + '/sys/split/file/getImage?fileName=' + imgData
})
}
}).finally(() => {
this.loading = false
})
},
/**
* 新增条款内容
* @param index
*/
handleAddClauseContent (index) {
if (index === -1) {
this.addIndex = this.contentList.length - 1
} else {
this.addIndex = index
}
this.$refs.splitDetailAddTypeCheck.open()
},
// 增加条款内容成功
handleAddOk (type, content) {
const headerArr = this.contentList.splice(0, this.addIndex + 1)
const footerArr = this.contentList.splice(-this.addIndex)
const insertArr = []
switch (type) {
case TypeOfClauseItem.TEXT.value:
for (let i = 0; i < content; i++) {
insertArr.push({
id: '',
type: TypeOfClauseItem.TEXT.value,
itemContent: ''
})
}
break
case TypeOfClauseItem.IMG.value:
insertArr[0] = {
id: '',
type: TypeOfClauseItem.IMG.value,
itemContent: content.file.response.result.url,
thumbUrl: window._CONFIG.domianURL + '/sys/split/file/getImage?fileName=' + content.file.response.result.url.split('fileName=')[1]
}
break
case TypeOfClauseItem.TABLE.value:
insertArr[0] = {
id: '',
type: 'TABLE',
itemContent: content[0]
}
}
this.contentList = [...headerArr, ...insertArr, ...footerArr]
},
/**
* 表格编辑
* @param index
* @param item
*/
splitEdit (index, item) {
this.addIndex = index
this.$refs.splitDetailAddTable.open(item.itemContent)
this.$refs.splitDetailAddTable.isTableEdit = true
},
/**
* 编辑表格类型成功
* @param ueContent
*/
editTableOk (ueContent) {
this.contentList[this.addIndex] = {
id: '',
type: 'TABLE',
itemContent: ueContent[0]
}
this.contentList = JSON.parse(JSON.stringify(this.contentList))
},
// 点击点击上传按钮
clickButtonToUpload (item) {
this.$refs.uploadFile.open(this.formInline[item.dbFieldName])
this.uploadName = item.dbFieldName
},
// 文件上传改变后的回调
uploadFileChange (data) {
const attIdList = []
if (data && data.length > 0) {
data.map(item => {
attIdList.push(item.id || data.name)
})
}
/** 赋值给当前对应的表单文件 */
this.formInline[this.uploadName] = attIdList.join(',')
this.formInline = { ...this.formInline }
},
// 正则替换小数点
limitNumber (value) {
if (typeof value === 'string') {
return !isNaN(Number(value)) ? value.replace(/\./g, '') : 0
} else if (typeof value === 'number') {
return !isNaN(value) ? String(value).replace(/\./g, '') : 0
} else {
return 0
}
},
/**
* 删除条款内容
* @param index
* @param clause
*/
handleDeleteClauseContent (index, clause) {
this.contentList.splice(index, 1)
if (clause.id) {
this.deleteIds.push(clause.id)
}
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.formInline = {}
this.contentList = []
this.interpretationArticlesList = []
},
handleSave () {
if (this.submitFlag) {
return
}
this.$refs.ruleForm.validate(valid => {
console.log(valid)
if (valid) {
this.submitFlag = true
this.loading = true
// 判断新增编辑
let submitFunc
if (this.formInline.id) {
submitFunc = editClause
} else {
submitFunc = addClause
}
// 处理请求参数
const formData = JSON.parse(JSON.stringify(this.formInline))
// 处理动态表单多选的值
Object.keys(formData).forEach(res => {
if (formData[res] instanceof Array) {
if (formData[res][0] instanceof Object) {
// 说明是树选择
formData[res] = formData[res].map(item => item.value).join(',')
} else {
formData[res] = formData[res].join(',')
}
}
})
formData.info_id = this.infoId
formData.menu_id = this.menuId
const contentList = JSON.parse(JSON.stringify(this.contentList))
contentList.forEach((item, index) => {
if (item.type === 'IMG') {
delete item.thumbUrl
}
})
formData.itemValEOList = contentList
formData.interpretationArticlesList = this.interpretationArticlesList.filter(tt => !!tt.itemContent)
formData.delItemIds = this.deleteIds.join(',')
submitFunc(formData).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
this.submitFlag = false
})
}
})
},
/**
* 新增一条条文解读
* @param index
*/
handleAddUnscramble (index) {
this.interpretationArticlesList.splice(index + 1, 0, { id: '', itemContent: null, displaySeq: 1 })
},
/**
* 删除条文解读
*/
handleDeleteUnscramble (index, item) {
this.interpretationArticlesList.splice(index, 1)
if (item.id) {
this.deleteIds.push(item.id)
}
},
/**
* 获取条文解读数据
*/
initInterpretationArticlesList () {
getInterpretationArticlesList({ id: this.itemVal.id }).then(res => {
if (res.success) {
if (res.result && res.result.length > 0) {
this.interpretationArticlesList = res.result
} else {
this.interpretationArticlesList = [{
itemContent: null,
displaySeq: 1
}]
}
}
})
},
updateDom () {
this.$forceUpdate()
},
filterTreeOption (input, option) {
const text = option.componentOptions.propsData.title || option.componentOptions.propsData.label
return text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
/**
* 属性标识的子集字段处理
* @param field
* @returns {null|string}
*/
getTreeSelectionChildrenColumnName (field) {
if (field.dbFieldName === 'property_tag') {
return 'childrenList'
}
return null
},
/**
* 如果字段中的usableRange只有一个值,那么就需要在placeholder中标识
* @param field
*/
getPlaceholderSuffix (field) {
let fieldUsableRange = []
if (field.usableRange) {
fieldUsableRange = field.usableRange.split(',')
}
if (fieldUsableRange.length === 1) {
return `${ClauseUsableRange.nameOfIndex(fieldUsableRange[0])}`
}
return ''
}
}
}
</script>
<style scoped lang="less">
.split-content {
.box-title-text {
line-height: 1.4;
display: flex;
.title-text {
width: 114px;
text-align: right;
display: inline-block;
font-weight: 500;
font-size: 14px;
margin-right: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
height: 42px;
line-height: 42px;
.Required {
color: red;
margin-right: 4px;
}
.title-text-text {
margin-top: 9px;
}
}
.item-model {
width: calc(100% - 130px);
display: inline-block;
margin-top: 2px;
}
}
.con-divider {
font-size: 16px;
font-weight: 500;
color: rgba(0, 0, 0, 0.85)
}
.table-operator {
margin-bottom: 20px;
text-align: right;
}
}
.split-content-row {
.split-row {
display: flex;
justify-content: space-between;
margin: 0 20px 20px;
.row-left {
flex: 1;
/*border:1px solid #9e9e9e;*/
img {
cursor: pointer;
width: 32px;
height: 32px;
}
.item-table {
/deep/ table {
width: 100% !important;
}
}
}
.row-right {
min-width: 150px;
margin-left: 20px;
.row-btn-add, .row-btn-edit {
margin-right: 20px;
}
}
}
}
.split-form-footer {
margin-top: 20px;
text-align: center;
position: fixed;
bottom: 0px;
right: 0;
width: 900px;
padding-bottom: 10px;
background: #FFFFFF;
.footer-btn-save {
margin-right: 20px;
}
}
.split-title {
.title-wraper {
.title-top {
display: flex;
justify-content: space-around;
.title-flex {
width: 75px;
height: 35px;
text-align: center;
border-radius: 5px;
border: 1px solid #d9d9d9;
padding: 7px;
cursor: pointer;
}
.title-flex-active {
background: #00B3BE;
border-color: #00B3BE;
color: #FFFFFF;
}
}
.title-num {
margin: 20px 20px 0;
line-height: 32px;
.title-num-add {
min-width: 70px;
}
.title-num-edit {
flex: 1;
}
}
}
}
.split-img {
margin-top: 20px;
text-align: center;
img {
}
}
/*用到的css代码*/
.form-flex-box {
display: flex;
flex-wrap: wrap;
}
.form-flex-item {
width: 100%;
}
.header-text {
display: flex;
justify-content: space-between;
align-items: center;
}
.clause-item {
display: flex;
justify-content: space-between;
margin: 0 0 20px 20px;
&-content {
width: 0;
flex: 1;
/deep/ table {
width: 100%;
border: 1px solid;
td {
border: 1px solid;
}
}
/deep/ img {
max-width: 100%;
max-height: 500px;
}
}
&-operate {
.ant-btn {
margin-left: 15px
}
}
}
.clause-item:last-child {
margin-bottom: 0;
}
/deep/ .ant-select-tree-dropdown {
max-height: 50vh !important;
}
.unscramble-item {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
&-content {
width: 0;
flex: 1;
}
&-operate {
.ant-btn {
margin-left: 15px
}
}
}
.unscramble-item:last-child {
margin-bottom: 0;
}
</style>
@@ -0,0 +1,53 @@
<template>
<j-modal
:title="title"
:visible="contentVisible"
:width="1000"
:confirm-loading="confirmLoading"
:footer="false"
switchFullscreen
@cancel="handleCancel"
>
<div class="clause-content" v-html="contentValue"></div>
</j-modal>
</template>
<script>
export default {
name: 'SplitClauseDetail',
data () {
return {
title: this.$t('docTool.split.clauseContent'),
contentVisible: false,
confirmLoading: false,
contentValue: ''
}
},
methods: {
open (val) {
this.contentVisible = true
this.contentValue = val
},
handleCancel () {
this.contentVisible = false
}
}
}
</script>
<style scoped lang="less">
.clause-content {
max-height: calc(100vh - 200px - 55px);
overflow: auto;
margin: 0 -24px;
padding: 0 24px;
/deep/ table {
width: 100%;
border: 1px solid;
td {
border: 1px solid;
}
}
}
</style>
@@ -0,0 +1,174 @@
<template>
<j-modal
:title="title"
class="split-table-title"
:visible="tableVisible"
switchFullscreen
:confirm-loading="confirmLoading"
@ok="handleOkTable"
@cancel="handleCancelTable"
:width="700"
>
<u-editor :content="ueContent" :config="config" ref="ueditor" @editor-onChange="uEditorChange"></u-editor>
</j-modal>
</template>
<script>
import UEditor from '../../../../components/uEditor/UEditor.vue'
export default {
name: 'SplitDetailAddTable',
components: { UEditor },
data () {
return {
title: this.$t('docTool.split.table'),
tableVisible: false,
confirmLoading: false,
ueContent: '',
// UE编辑器配置
config: {
// 可以在此处定义工具栏的内容
toolbars: [
[
'undo', // 撤销
'redo', // 重做
'bold', // 加粗
'italic', // 斜体
'underline', // 下划线
'strikethrough', // 删除线
'subscript', // 下标
'superscript', // 上标
'fontborder', // 字符边框
'fontfamily', // 字体
'fontsize', // 字号
'forecolor', // 字体颜色
// 'backcolor', // 背景色
'paragraph', // 段落格式
'customstyle', // 自定义标题
'indent', // 首行缩进
'justifyleft', // 居左对齐
'fullscreen', // 全屏
'justifyright', // 居右对齐
'justifycenter', // 居中对齐
'justifyjustify', // 两端对齐
'lineheight', // 行间距,
'rowspacingtop', // 段前距
'rowspacingbottom', // 段后距
'directionalityltr', // 从左向右输入
'directionalityrtl', // 从右向左输入
'cleardoc', // 清空文档
'formatmatch', // 格式刷
'removeformat', // 清除格式
'source', // 源代码
'blockquote', // 引用
'pasteplain', // 纯文本粘贴模式
'selectall', // 全选
'horizontal', // 分隔线
'time', // 时间
'date', // 日期
'link', // 超链接
'unlink', // 取消链接
// 'background', // 背景
// 'simpleupload', // 单图上传
'imagenone', // 默认
'imageleft', // 左浮动
'imageright', // 右浮动
'imagecenter', // 居中
'touppercase', // 字母大写
'tolowercase', // 字母小写
'emotion', // 表情
'spechars', // 特殊字符
'searchreplace', // 查询替换
'insertunorderedlist', // 无序列表
'insertorderedlist', // 有序列表
'pagebreak', // 分页
'insertframe', // 插入Iframe
'charts', // 图表
'edittip ', // 编辑提示
'autotypeset', // 自动排版
'drafts', // 从草稿箱加载
'inserttable', // 插入表格
'deletetable', // 删除表格,
'insertparagraphbeforetable', // ”表格前插入行”
'insertrow', // 前插入行
'insertcol', // 前插入列
'mergeright', // 右合并单元格
'mergedown', // 下合并单元格
'deleterow', // 删除行
'deletecol', // 删除列
'splittorows', // 拆分成行
'splittocols', // 拆分成列
'splittocells', // 完全拆分单元格
'inserttitle', // 插入标题
'deletecaption', // 删除表格标题,
'mergecells', // 合并多个单元格
'edittable', // 表格属性
'edittd' // 单元格属性
]
],
autoHeightEnabled: false,
autoFloatEnabled: false,
initialContent: '', // 初始化编辑器的内容,也可以通过textarea/script给值,看官网例子
autoClearinitialContent: true, // 是否自动清除编辑器初始内容,注意:如果focus属性设置为true,这个也为真,那么编辑器一上来就会触发导致初始化的内容看不到了
initialFrameWidth: 650,
elementPathEnabled: false,
initialFrameHeight: 300,
maximumWords: 4000,
BaseUrl: '',
UEDITOR_HOME_URL: '/ueditor/',
editTableIndex: 1
},
isTableEdit: false
}
},
methods: {
open (tableStr) {
console.log('$$$$$$$$$$$$', tableStr)
this.tableVisible = true
this.ueContent = tableStr
this.$nextTick(() => {
this.$refs.ueditor.setContent(this.ueContent)
})
},
// 表格确认按钮
handleOkTable () {
let ueContent = []
ueContent = this.ueContent.match(/<table.{0,}?.+?>{0,}?<\/table>/g) || []
if (ueContent && ueContent.length > 1) {
this.$message.warning(this.$t('oneTableAdded'))
} else if (!ueContent || ueContent.length < 1) {
this.$message.warning(this.$t('addATable'))
} if (ueContent && ueContent.length === 1) {
console.log(ueContent)
console.log(this.isTableEdit)
if (this.isTableEdit) {
this.$emit('editOk', ueContent)
} else {
this.$emit('addOk', ueContent)
}
this.close()
}
},
// 表格取消按钮
handleCancelTable () {
this.close()
},
close () {
this.tableVisible = false
this.isTableEdit = false
this.ueContent = ''
this.$nextTick(() => {
this.$refs.ueditor.execCommand()
})
},
// 获取表格内容
uEditorChange (val) {
console.log('--------------uEditorChange------------', val)
this.ueContent = val
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,296 @@
<template>
<j-modal
:title="$t('add')"
class="split-title"
:visible="visible"
:confirmLoading="confirmLoading"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel">
<div class="title-wraper">
<div class="title-top">
<span class="title-flex title-text-flex"
:class="{'title-flex-active':selected===TypeOfClauseItem.TEXT.value}"
@click="selectTitle(TypeOfClauseItem.TEXT.value)">{{ $t('docTool.split.text') }}</span>
<span class="title-flex title-pic"
:class="{'title-flex-active':selected===TypeOfClauseItem.IMG.value}"
@click="selectTitle(TypeOfClauseItem.IMG.value)">{{ $t('docTool.split.picture') }}</span>
<span class="title-flex title-table"
:class="{'title-flex-active':selected===TypeOfClauseItem.TABLE.value}"
@click="selectTitle(TypeOfClauseItem.TABLE.value)">{{ $t('docTool.split.table') }}</span>
</div>
<!-- 选择文本类型时需要填写个数 -->
<div class="title-num" v-if="selected===TypeOfClauseItem.TEXT.value">
<a-form>
<a-form-model
class="number-content"
:model="numberForm"
:rules="validatorRules"
ref="numberForm"
>
<a-form-model-item :label="$t('docTool.split.addQuantity')" prop="textNumber">
<a-input-number
style="width: 100%"
:min="0"
:max="100"
v-model="numberForm.textNumber"
:placeholder="$t('docTool.split.enterQuantity')"
:formatter="limitNumber"
:parser="limitNumber"/>
</a-form-model-item>
</a-form-model>
</a-form>
</div>
<!-- 选择表格时需要填写行列数 -->
<div class="title-num" v-if="selected===TypeOfClauseItem.TABLE.value">
<a-form>
<a-form-model
class="number-content"
:model="rowColForm"
:rules="validatorRowColRules"
ref="numberRowColForm"
>
<a-form-model-item :label="$t('docTool.split.numberRows')" prop="rowCount">
<a-input-number
style="width: 100%"
v-model="rowColForm.rowCount"
:min="1"
:max="100"
:placeholder="$t('pleaseEnter')+$t('docTool.split.numberRows')"
:formatter="limitNumber"
:parser="limitNumber"
/>
</a-form-model-item>
<a-form-model-item :label="$t('docTool.split.numberColumns')" prop="colCount">
<a-input-number
style="width: 100%"
v-model="rowColForm.colCount"
:min="1"
:max="100"
:placeholder="$t('pleaseEnter')+$t('docTool.split.numberColumns')"
:formatter="limitNumber"
:parser="limitNumber"
/>
</a-form-model-item>
</a-form-model>
</a-form>
</div>
</div>
<!-- 上传图片弹框 -->
<split-add-clause-upload-image
ref="uploadFile"
:disabled="disabled"
:title="titleImg"
:listType="listType"
:accept="accept"
@uploadSuccess="uploadSuccess">
</split-add-clause-upload-image>
<!-- 表格弹框 -->
<split-detail-add-table ref="splitDetailAddTable" @addOk="tableAddOk"></split-detail-add-table>
</j-modal>
</template>
<script>
// 上传图片弹框
import SplitAddClauseUploadImage from '../../../../components/splitUpload/SplitAddClauseUploadImage.vue'
// 编辑表格的弹框
import SplitDetailAddTable from './SplitDetailAddTable.vue'
import { TypeOfClauseItem } from '../../../../enums/commonEnums'
export default {
name: 'SplitDetailAddTypeCheck',
components: { SplitAddClauseUploadImage, SplitDetailAddTable },
watch: {
selected (val) {
if (this.selected === TypeOfClauseItem.TEXT.value) {
this.selectedNum = true
} else {
this.selectedNum = false
}
}
},
data () {
return {
TypeOfClauseItem,
visible: false,
confirmLoading: false,
selected: TypeOfClauseItem.TEXT.value,
selectedNum: true,
numberForm: {},
validatorRules: {
textNumber: [
{ required: true, message: this.$t('docTool.split.enterQuantity'), trigger: 'blur' }
]
},
// 文件上传弹框相关
splitVisible: false,
disabled: false,
titleImg: this.$t('uploadPictures'),
listType: 'picture',
accept: '.png,.jpeg,.jpg,.gif',
// 表格行列相关数据
rowColForm: {},
validatorRowColRules: {
colCount: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('docTool.split.numberRows'), trigger: 'blur' }
],
rowCount: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('docTool.split.numberColumns'), trigger: 'blur' }
]
}
}
},
methods: {
open () {
this.visible = true
},
selectTitle (val) {
this.selected = val
if (val === TypeOfClauseItem.TEXT.value) {
this.$nextTick(() => {
this.$refs.numberForm && this.$refs.numberForm.clearValidate()
})
} else if (TypeOfClauseItem.TABLE.value) {
this.$nextTick(() => {
this.$refs.numberRowColForm && this.$refs.numberRowColForm.clearValidate()
})
}
},
// 上传文件
uploadSuccess (file, data, imgs) {
this.$emit('ok', TypeOfClauseItem.IMG.value, { file, data, imgs })
this.visible = false
// this.$emit('addImageOk', file, data, imgs)
},
// 确定增加的类型
handleOk () {
if (this.confirmLoading) {
return
}
if (this.selected === TypeOfClauseItem.TEXT.value) { // 文本类型
this.$refs.numberForm.validate(valid => {
if (valid) {
this.confirmLoading = true
this.$emit('ok', TypeOfClauseItem.TEXT.value, this.numberForm.textNumber)
this.visible = false
this.confirmLoading = false
this.numberForm.textNumber = null
}
})
return
}
if (this.selected === TypeOfClauseItem.IMG.value) { // 图片类型
this.$refs.uploadFile.open()
// this.visible = false
return
}
if (this.selected === TypeOfClauseItem.TABLE.value) { // 表格类型
this.$refs.numberRowColForm.validate(valid => {
if (valid) {
if (this.rowColForm.rowCount > 0 && this.rowColForm.colCount > 0) {
const tableList = []
let tableStr = ''
tableStr = '<table class="word-table" border="1" cellpadding="0" cellspacing="0">'
for (let r = 0; r < this.rowColForm.rowCount; r++) {
const rowTableList = []
tableStr += '<tr>'
for (let c = 0; c < this.rowColForm.colCount; c++) {
tableStr += `<td></td>`
const tableItem = {}
tableItem.id = ''
tableItem.rowNum = r + 1
tableItem.colNum = c + 1
tableItem.content = ''
rowTableList.push(tableItem)
}
tableStr += `</tr>`
tableList.push(rowTableList)
}
tableStr += `</table>`
this.$nextTick(() => {
this.$refs.splitDetailAddTable.isTableEdit = false
this.$refs.splitDetailAddTable.open(tableStr)
})
// this.visible = false
this.rowColForm = {}
}
}
})
}
},
handleCancel () {
this.visible = false
},
// 添加表格的回调
tableAddOk (ueContent) {
this.$emit('ok', TypeOfClauseItem.TABLE.value, ueContent)
this.visible = false
},
// 正则替换小数点
limitNumber (value) {
if (typeof value === 'string') {
return !isNaN(Number(value)) ? value.replace(/\./g, '') : 0
} else if (typeof value === 'number') {
return !isNaN(value) ? String(value).replace(/\./g, '') : 0
} else {
return 0
}
}
}
}
</script>
<style scoped lang="less">
.split-title {
.title-wraper {
.title-top {
display: flex;
justify-content: space-around;
.title-flex {
width: 75px;
height: 35px;
text-align: center;
border-radius: 5px;
border: 1px solid #d9d9d9;
padding: 7px;
cursor: pointer;
}
.title-flex-active {
background: @primary-color;
border-color: @primary-color;
color: #FFFFFF;
}
}
.title-num {
margin: 20px 20px 0;
line-height: 32px;
.title-num-add {
min-width: 70px;
}
.title-num-edit {
flex: 1;
}
}
}
}
.number-content {
.ant-form-item {
display: flex;
.ant-form-item-control-wrapper {
flex: 1;
.title-num-edit {
width: 100%;
}
}
}
}
</style>
@@ -0,0 +1,511 @@
<template>
<a-drawer
:title="title"
placement="right"
:visible="visible"
@close="handleCancel"
width="900"
class="custom-drawer-style">
<div class="custom-drawer-style-scroll">
<a-spin :spinning="confirmLoading">
<a-form-model :model="formInline" v-if="isFormInline" class="form-add" :rules="rules" ref="ruleForm">
<div class="form-flex-box">
<template v-for="(item, index) in dataList">
<div :key="index"
class="box-title-text form-flex-item"
:class="{'form-flex-item-line': item.fieldShowType === FieldType.STANDARD_MORE.value}"
v-if="!hiddenFieldList.includes(item.dbFieldName)">
<div class="title-text">
<span class="required" v-if="item.fieldMustInput === '1'">*</span>
<span class="title-text-text" :title="item.dbFieldTxt">{{ item.dbFieldTxt }}</span>
</div>
<a-form-model-item class="item-model" :prop="item.dbFieldName">
<!--条文解读-->
<!--<template v-if="item.dbFieldName === 'interpretation_articles'">-->
<!-- <div class="unscramble-item"-->
<!-- v-for="(unscramble, unscrambleIndex) in interpretationArticlesList"-->
<!-- :key="unscrambleIndex + 'interpretation_articles'">-->
<!-- <div class="unscramble-item-content">-->
<!-- &lt;!&ndash;文本&ndash;&gt;-->
<!-- <a-input type="textarea" :maxLength="500" v-model="unscramble.itemContent"/>-->
<!-- </div>-->
<!-- <div class="unscramble-item-operate">-->
<!-- <a-button @click="handleAddUnscramble(unscrambleIndex)">{{ $t('newlyAdded') }}</a-button>-->
<!-- <a-button @click="handleDeleteUnscramble(unscrambleIndex, unscramble)">{{ $t('delete') }}</a-button>-->
<!-- </div>-->
<!-- </div>-->
<!--</template>-->
<!-- 1, 日期单选 -->
<a-date-picker v-if="item.fieldShowType === FieldType.DATE_SINGLE.value"
class="box-input"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:getCalendarContainer="(trigger) => trigger.parentNode"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
style="width: 100%" />
<!-- 2, 单选用户 -->
<user-selection v-else-if="item.fieldShowType === FieldType.USER_SINGLE.value"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
v-model="formInline[item.dbFieldName]"
:filedName="item.dbFieldName"
@nameChange="userSelectNameChange"
:nameStr="formInline[item.dbFieldName + '_dictText']"
type="radio"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)" />
<!-- 3, 单选组织机构 -->
<j-select-depart v-else-if="item.fieldShowType === FieldType.ORGAN_SINGLE.value"
v-model="formInline[item.dbFieldName]"
:backDepart="true" />
<!-- 4, 日期多选 -->
<j-multiple-date-picker v-else-if="item.fieldShowType === FieldType.DATE_MORE.value"
class="box-input"
v-model="formInline[item.dbFieldName]"
:fieldName="item.dbFieldName"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)" />
<!-- 5, 多行文本 -->
<a-textarea v-else-if="item.fieldShowType === FieldType.TEXTAREA.value"
:placeholder="$t('pleaseEnter')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:maxLength="500"
v-model="formInline[item.dbFieldName]" :rows="4" />
<!-- 6, 标准多选 -->
<standard-selection v-else-if="item.fieldShowType === FieldType.STANDARD_MORE.value"
:source="flag"
:disabledSourceSearch="true"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
v-model="formInline[item.dbFieldName]" />
<!-- 7, 多选组织机构 -->
<j-select-depart v-else-if="item.fieldShowType === FieldType.ORGAN_MORE.value"
v-model="formInline[item.dbFieldName]"
:multi="true"
:backDepart="true"
:treeOpera="true" />
<!-- 8, 上传文件 -->
<a-button v-else-if="item.fieldShowType === FieldType.FILE_UP.value" type="primary" class="button-text"
@click="clickButtonToUpload(item)">
{{
(formInline[item.dbFieldName] === 'null' || formInline[item.dbFieldName] === ''
|| formInline[item.dbFieldName] === null || formInline[item.dbFieldName] === undefined)
? $t('uploadFile.clickUpload')
: $t('uploadFile.viewUploadedFiles')
}}
</a-button>
<!-- 9, 文本框 -->
<a-input v-else-if="item.fieldShowType === FieldType.TEXT_BOX.value"
class="box-input"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
v-model="formInline[item.dbFieldName]"
:maxLength="50"
:placeholder="(specialPlaceholder[item.dbFieldName] || $t('pleaseEnter')+item.dbFieldTxt) + getPlaceholderSuffix(item)" />
<!-- 10, 下拉单选数据字典 -->
<j-dict-select-tag v-else-if="item.fieldShowType === FieldType.OPTION_SINGLE.value"
class="box-input"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:type="radioDictSelect.includes(item.dbFieldName)? 'radio' : 'select'"
:triggerChange="false"
:dictCode="item.dictField" />
<!-- 11, 下拉多选数据字典 -->
<j-multi-select-tag v-else-if="item.fieldShowType === FieldType.OPTION_MORE.value"
class="box-input"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
:type="'select'"
:triggerChange="false"
:dictCode="item.dictField" />
<!-- 12, 树选择 -->
<s-tree-select
v-else-if="item.fieldShowType === FieldType.TREE.value"
v-model="formInline[item.dbFieldName]"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:tree-data="optionsData[item.dbFieldName]"
tree-checkable
treeCheckStrictly
:label-in-value="false"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)" />
<!-- 14, 年份选择 -->
<j-date v-else-if="item.fieldShowType === FieldType.YEAR_PICKER.value"
show-type="year"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
v-model="formInline[item.dbFieldName]"
:default-value="defaultValue[item.dbFieldName]"
date-format="YYYY"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)" />
<!-- 15, 树选择单选 -->
<s-tree-select v-else-if="item.fieldShowType === FieldType.TREE_SINGLE.value"
v-model="formInline[item.dbFieldName]"
:getPopupContainer="triggerNode=> triggerNode.parentNode"
style="width: 100%"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:tree-data="optionsData[item.dbFieldName]"
:label-in-value="false"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)" />
<!--16树形弹框选择-->
<tree-selection v-else-if="item.fieldShowType === FieldType.TREE_MODAL_SELECT.value"
:placeholder="$t('pleaseSelect')+item.dbFieldTxt + getPlaceholderSuffix(item)"
v-model="formInline[item.dbFieldName]"
:filedName="item.dbFieldName"
type="checkbox"
:options-href="item.fieldHref"
:nameStr="formInline[item.dbFieldName + '_dictText']"
:dict-id="item.dictId"
:disabled="disabled || disabledFieldList.includes(item.dbFieldName)"
:children-column-name="getTreeSelectionChildrenColumnName(item)"
:is-tag-library-restriction="item.dbFieldName === 'property_tag'"
:search-field-name="item.dbFieldName === 'property_tag' ? 'firstNodeName' : null" />
</a-form-model-item>
</div>
</template>
</div>
<upload-file ref="uploadFile" @change="uploadFileChange" :return-url="false"></upload-file>
</a-form-model>
</a-spin>
</div>
<div class="custom-drawer-style-bottom-btn">
<!--取消-->
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
<!--保存-->
<a-button type="primary" class="footer-btn-save" @click="handleSave">{{ $t('preservation') }}</a-button>
</div>
</a-drawer>
</template>
<script>
import JMultipleDatePicker from '../../../../components/JMultipleDatePicker.vue'
import UploadFile from '../../../../components/UploadFile.vue'
import StandardSelection from '../../../../components/selection/StandardSelection.vue'
import UserSelection from '../../../../components/selection/UserSelection.vue'
import { getDocSplitEditForm, editBatchEditClause } from '../../../../api/documentToolApi'
import { ClauseUsableRange, FieldType, YesOrNoEnum } from '../../../../enums/commonEnums'
import { getFormDictTreeList } from '../../../../api/api'
import { getAction } from '../../../../api/manage'
import STreeSelect from '../../../../components/STreeSelect'
import TreeSelection from '../../../../components/selection/TreeSelection'
export default {
name: 'SplitDetailBatchEditDrawer',
components: { TreeSelection, UserSelection, StandardSelection, UploadFile, JMultipleDatePicker, STreeSelect },
data () {
return {
FieldType,
title: this.$t('docTool.split.batchEdit'),
visible: false,
confirmLoading: false,
disabled: false,
formInline: {},
isFormInline: false,
dataList: [],
ruleList: [],
rules: {},
uploadName: '',
type: 'add',
hiddenFieldList: ['item_num', 'item_title', 'item_content', 'interpretation_articles'], // 隐藏的字段
radioDictSelect: ['is_long_effect'], // 使用radio样式的数据字典单选
disabledFieldList: [], // 禁用的字段
specialPlaceholder: {}, // 特殊的placeholder
optionsData: {} // 通过接口获取的下拉数据
// interpretationArticlesList: [] // 条文解读数据
}
},
watch: {
// 是否长期有效,是的话新认证实施日期、已认证实施日期、新注册实施日期为多选,否则为单选
'formInline.is_long_effect': {
handler (value) {
console.log('是否长期有效', value)
const indexArr = []
// 新认证实施日期
indexArr[0] = this.dataList.findIndex(tt => tt.dbFieldName === 'new_cer_implement_date')
// 已认证实施日期
indexArr[1] = this.dataList.findIndex(tt => tt.dbFieldName === 'cer_implement_date')
// 新注册实施日期
indexArr[2] = this.dataList.findIndex(tt => tt.dbFieldName === 'new_register_implement_date')
if (value === YesOrNoEnum.YES.value) {
indexArr.forEach(index => {
this.$set(this.dataList[index], 'fieldShowType', FieldType.DATE_MORE.value)
})
} else if (value === YesOrNoEnum.NO.value) {
indexArr.forEach(index => {
this.$set(this.dataList[index], 'fieldShowType', FieldType.DATE_SINGLE.value)
})
}
this.$forceUpdate()
},
immediate: true
}
},
methods: {
open (ids) {
this.visible = true
this.formInline = {}
this.formInline.ids = ids
this.getForm()
},
/**
* 获取表单字段
* @param callBack
*/
getForm (callBack) {
this.loading = true
const params = {
flag: this.flag,
type: this.type
}
getDocSplitEditForm(params).then((res) => {
if (res.success) {
const indexArr = []
// 新认证实施日期
indexArr[0] = res.result.findIndex(tt => tt.dbFieldName === 'new_cer_implement_date')
// 已认证实施日期
indexArr[1] = res.result.findIndex(tt => tt.dbFieldName === 'cer_implement_date')
// 新注册实施日期
indexArr[2] = res.result.findIndex(tt => tt.dbFieldName === 'new_register_implement_date')
// 条文解读数据处理
// this.interpretationArticlesList = [
// { id: '', itemContent: null }
// ]
// 是否长期有效默认选否
this.$set(this.formInline, 'is_long_effect', YesOrNoEnum.NO.value)
this.dataList = res.result
this.ruleList = res.result
this.integrateData()
this.getOptionsData()
callBack && callBack()
}
}).finally(() => {
this.loading = false
})
},
/**
* 处理需要通过接口获取的下拉数据
*/
getOptionsData () {
const formList = Object.values(this.dataList).reduce((acc, cur) => acc.concat(cur), [])
formList.forEach(item => {
if (item.fieldShowType === FieldType.TREE.value || item.fieldShowType === FieldType.TREE_SINGLE.value) {
// 展示类型是树选择,且有接口的
if (item.fieldHref) {
getAction(item.fieldHref).then(res => {
if (res.success) {
this.$set(this.optionsData, item.dbFieldName, res.result)
}
})
} else if (item.dictId) {
getFormDictTreeList({ dictId: item.dictId }).then(res => {
if (res.success) {
this.$set(this.optionsData, item.dbFieldName, res.result)
}
})
}
}
})
},
/**
* 设置表单校验信息
*/
integrateData () {
this.isFormInline = false
const rules = {}
this.ruleList.forEach((res, index) => {
const rule = []
if (res.field_must_input === 1) {
if (res.field_show_type === '1') {
rule.push({
required: true,
message: res.db_field_txt + this.$t('cannotEmpty'),
trigger: 'blur'
})
} else if (res.field_show_type === '4' || res.field_show_type === '6' ||
res.field_show_type === '5' || res.field_show_type === '7'
) {
rule.push({
required: true,
message: res.db_field_txt + this.$t('cannotEmpty'),
trigger: 'change'
})
} else if (res.field_show_type === '2') {
rule.push({
required: true,
message: res.db_field_txt + this.$t('cannotEmpty'),
trigger: 'blur'
})
} else if (res.field_show_type === '8' || res.field_show_type === '9' || res.field_show_type === '10' || res.field_show_type === '11') {
rule.push({
required: true,
message: res.db_field_txt + this.$t('cannotEmpty'),
trigger: 'blur'
})
} else if (res.field_show_type === '3') {
rule.push({
required: true,
message: res.db_field_txt + this.$t('cannotEmpty'),
trigger: 'change'
})
}
}
if (res.field_show_type === '1' ||
res.field_show_type === '8' || res.field_show_type === '9' || res.field_show_type === '10' ||
res.field_show_type === '11') {
rule.push({
max: res.db_length,
message: res.db_field_txt + this.$t('cantExeed') + res.db_length + this.$t('characters'),
trigger: 'blur'
})
}
if (rule.length > 0) {
rules[res.db_field_name] = rule
}
})
this.$set(this, 'rules', rules)
// console.log('rules',this.rules)
this.isFormInline = true
this.loading = false
// console.log('this.column',this.column)
},
// 点击点击上传按钮
clickButtonToUpload (item) {
this.$refs.uploadFile.open(this.formInline[item.db_field_name])
this.uploadName = item.db_field_name
},
// 文件上传改变后的回调
uploadFileChange (data) {
const attIdList = []
if (data && data.length > 0) {
data.map(item => {
attIdList.push(item.id || data.name)
})
}
/** 赋值给当前对应的表单文件 */
this.formInline[this.uploadName] = attIdList.join(',')
this.formInline = { ...this.formInline }
},
handleSave () {
if (this.confirmLoading) {
return
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
this.confirmLoading = true
// 处理请求参数
const formData = JSON.parse(JSON.stringify(this.formInline))
// 处理动态表单多选的值
Object.keys(formData).forEach(res => {
if (formData[res] instanceof Array) {
if (formData[res][0] instanceof Object) {
// 说明是树选择
formData[res] = formData[res].map(item => item.value).join(',')
} else {
formData[res] = formData[res].join(',')
}
}
})
// 处理条文解读数据
// formData.interpretationArticlesList = this.interpretationArticlesList
editBatchEditClause(formData).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.formInline = {}
},
/**
* 新增一条条文解读
* @param index
*/
handleAddUnscramble (index) {
this.interpretationArticlesList.splice(index + 1, 0, { id: '', itemContent: null })
},
/**
* 删除条文解读
*/
handleDeleteUnscramble (index, item) {
this.interpretationArticlesList.splice(index, 1)
if (item.id) {
this.deleteIds.push(item.id)
}
},
filterTreeOption (input, option) {
const text = option.componentOptions.propsData.title || option.componentOptions.propsData.label
return text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
/**
* 属性标识的子集字段处理
* @param field
* @returns {null|string}
*/
getTreeSelectionChildrenColumnName (field) {
if (field.dbFieldName === 'property_tag') {
return 'childrenList'
}
return null
},
/**
* 如果字段中的usableRange只有一个值,那么就需要在placeholder中标识
* @param field
*/
getPlaceholderSuffix (field) {
const fieldUsableRange = (field.usableRange || '').split(',')
if (fieldUsableRange.length === 1) {
return `${ClauseUsableRange.nameOfIndex(fieldUsableRange[0])}`
}
return ''
}
}
}
</script>
<style scoped lang="less">
.form-flex-box {
display: flex;
flex-wrap: wrap;
}
.form-flex-item {
width: 100%;
}
.unscramble-item {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
&-content {
width: 0;
flex: 1;
table {
width: 100%;
}
}
&-operate {
.ant-btn {
margin-left: 15px
}
}
}
.unscramble-item:last-child {
margin-bottom: 0;
}
</style>
@@ -0,0 +1,151 @@
<template>
<j-modal
class="add-menus"
:title="title"
:visible="visible"
:confirm-loading="confirmLoading"
switchFullscreen
@cancel="handleCancel"
@ok="handleOk">
<a-form-model
class="split-click-right-form"
ref="ruleForm"
:model="form"
:rules="rules"
:label-col="labelCol"
:wrapper-col="wrapperCol">
<!--条款号-->
<a-form-model-item :label="$t('docTool.split.clauseNo')" prop="name">
<a-input class="box-input"
v-model="form.name"
:placeholder="$t('pleaseEnter')+$t('docTool.split.clauseNo')"
:maxLength="50" />
</a-form-model-item>
<!--条款名称-->
<a-form-model-item :label="$t('docTool.split.clauseName')" prop="itemName">
<a-input class="box-input"
v-model="form.itemName"
:placeholder="$t('pleaseEnter')+$t('docTool.split.clauseName')"
:maxLength="50" />
</a-form-model-item>
<!--序号-->
<a-form-model-item :label="$t('serialNumber')" prop="displaySeq">
<a-input-number v-model="form.displaySeq" :min="1" :max="99999"
class="box-input"
:placeholder="$t('pleaseEnter')+$t('serialNumber')"
:formatter="limitNumber"
:parser="limitNumber" style="width: 100%" />
</a-form-model-item>
</a-form-model>
</j-modal>
</template>
<script>
import { addClauseTreeNode, editClauseTreeNode } from '../../../../api/documentToolApi'
export default {
name: 'SplitDetailNodeAdd',
data () {
return {
title: this.$t('add'),
visible: false,
confirmLoading: false,
labelCol: { span: 6 },
wrapperCol: { span: 16 },
form: {},
rules: {
name: [
// { required: true, message: this.$t('PleaseEnter') + this.$t('clauseNo'), trigger: 'blur' },
],
itemName: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('docTool.split.clauseName'), trigger: 'blur' }
],
displaySeq: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('serialNumber'), trigger: 'blur' }
// { min: 1, max: 99999, message: this.$t('cantExeed') + '99999' + this.$t('characters'), trigger: 'blur' },
]
},
nodeItem: {},
infoId: ''
}
},
methods: {
add (nodeItem, infoId) {
this.title = this.$t('add')
this.form = {}
this.visible = true
this.nodeItem = nodeItem
this.infoId = infoId
},
edit (nodeItem, infoId) {
this.title = this.$t('edit')
this.nodeItem = nodeItem
this.form = Object.assign({}, nodeItem)
this.infoId = infoId
this.visible = true
},
handleCancel () {
this.visible = false
},
// 增加节点确定
handleOk () {
if (this.confirmLoading) {
return
}
this.$refs.ruleForm.validate(valid => {
if (valid) {
const expandId = []
expandId.push(this.form.pid)
let submitFunc
const params = Object.assign({
infoId: this.infoId,
pid: this.nodeItem.id
}, this.form)
if (this.form.id) {
submitFunc = editClauseTreeNode
} else {
submitFunc = addClauseTreeNode
}
this.confirmLoading = true
submitFunc(params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.form = {}
this.visible = false
if (this.form.id) {
this.$emit('ok', expandId)
} else {
this.$emit('ok')
}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
// 正则替换小数点
limitNumber (value) {
if (typeof value === 'string') {
return !isNaN(Number(value)) ? value.replace(/\./g, '') : 0
} else if (typeof value === 'number') {
return !isNaN(value) ? String(value).replace(/\./g, '') : 0
} else {
return 0
}
}
}
}
</script>
<style scoped lang="less">
.add-menus {
.ant-modal-wrap {
.ant-modal-footer {
text-align: center;
}
}
}
</style>
@@ -0,0 +1,200 @@
<template>
<a-drawer
:title="$t('docTool.split.splitText')"
placement="right"
:visible="visible"
@close="onClose"
width="1100"
class="custom-drawer-style">
<div class="custom-drawer-style-scroll">
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-row :gutter="24">
<!--来源-->
<a-col :md="8" :sm="12">
<a-form-item :label="$t('standardSelect.source')">
<j-dict-select-tag
class="box-input"
v-model="queryParam.origin"
:placeholder="$t('pleaseSelect') + $t('standardSelect.source')"
:type="'select'"
:triggerChange="false"
:dictCode="'split_standard_source'" />
</a-form-item>
</a-col>
<!--标准号-->
<a-col :md="8" :sm="12">
<a-form-item :label="$t('standardSelect.standardNumber')">
<a-input :placeholder="$t('pleaseEnter') + $t('standardSelect.standardNumber')"
v-model="queryParam.standardNumber"></a-input>
</a-form-item>
</a-col>
<!--标准名称-->
<a-col :md="8" :sm="12">
<a-form-item :label="$t('standardSelect.standardName')">
<a-input :placeholder="$t('pleaseEnter') + $t('standardSelect.standardName')"
v-model="queryParam.standardName"></a-input>
</a-form-item>
</a-col>
<!--文本状态-->
<a-col :md="8" :sm="12">
<a-form-item :label="$t('docTool.comparison.textStatus')">
<j-dict-select-tag
class="box-input"
v-model="queryParam.standardFileType"
:placeholder="$t('pleaseSelect') + $t('docTool.comparison.textStatus')"
:type="'select'"
:triggerChange="false"
:dictCode="'process_manuscript'" />
</a-form-item>
</a-col>
</a-row>
</a-form>
<div class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">
{{ $t('query') }}
</a-button>
<a-button type="primary" @click="searchReset" icon="reload" ghost style="margin-left: 8px">{{ $t('reset') }}</a-button>
</div>
</div>
<j-table
:columns="columns"
:dataSource="dataSource"
:can-drag="true"
rowKey="id"
:loading="loading"
:scroll="{x: '100%'}"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange, type: 'checkbox' }"
:pagination="ipagination"
@change="handleTableChange">
</j-table>
</div>
<div class="custom-drawer-style-bottom-btn">
<a-button @click="cancelSplit" style='margin-right: 15px'>{{ $t('cancel') }}</a-button>
<a-button type="primary" :loading="loading" @click="submitSplit">{{ $t('docTool.split.split') }}</a-button>
</div>
</a-drawer>
</template>
<script>
import JTable from '../../../../components/jero/JTable.vue'
import { JeroListMixin } from '../../../../mixins/JeroListMixin.js'
import { addDocSplit } from '../../../../api/documentToolApi.js'
export default {
name: 'SplitText',
components: { JTable },
mixins: [JeroListMixin],
data () {
return {
visible: false,
loading: false,
labelCol: {
span: 6
},
wrapperCol: {
span: 14
},
columns: [
// 资料类型
{
dataIndex: 'origin_dictText',
title: this.$t('docTool.split.dataType'),
width: 150
},
// 标准号
{
dataIndex: 'standardNumber',
title: this.$t('standardSelect.standardNumber'),
width: 150
},
// 标准名称
{
dataIndex: 'standardName',
title: this.$t('standardSelect.standardName'),
width: 150
},
// 文件状态
{
dataIndex: 'standardFileType_dictText',
title: this.$t('docTool.comparison.textStatus'),
width: 150
},
// 附件
{
dataIndex: 'fileName',
title: this.$t('businessSupport.questionAnswer.attach'),
width: 150
}
],
url: {
list: '/laws/DocumentTool/documentSplit/querySplitFileInfoByPage' // 表格数据
},
type: 'pdfdoc',
operationList: [],
showAction: false,
flag: 'header',
disableMixinCreated: true
}
},
methods: {
open () {
this.visible = true
this.queryParam = {}
this.loadData(1)
},
// 抽屉关闭
onClose () {
this.visible = false
this.onClearSelected()
},
// 取消
cancelSplit () {
this.onClose()
},
// 拆分
submitSplit () {
if (this.loading) {
return
}
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectOne'))
return
}
// 最多同时拆分十条
if (this.selectedRowKeys.length > 10) {
this.$message.warning(this.$t('docTool.split.maximumSelection') + '10' + this.$t('docTool.split.pieceOfData'))
return
}
this.loading = true
addDocSplit({ ids: this.selectedRowKeys.join(',') }).then(res => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.onClose()
} else {
this.messageLineFeedByBr(this.$t('operationFailed'), res.message)
}
this.$emit('ok')
}).finally(() => {
this.loading = false
})
}
}
}
</script>
<style scoped lang="less">
@import '~../../../../assets/less/common.less';
.table-page-search-wrapper {
display: flex;
.ant-form {
flex: 1;
}
}
.table-page-search-submitButtons {
margin-left: 10px;
}
</style>
@@ -0,0 +1,220 @@
<template>
<a-card :bordered="false">
<div class="table-operator">
<!-- 上传 -->
<a-button icon="plus" type="primary" @click="handleAdd" v-has="'docTool:ocrTextRecognition:add'">{{$t('docTool.ocrTextRecognition.upload') }}</a-button>
<!-- 批量删除 -->
<a-button type="primary" icon="delete" ghost @click="batchDel" v-has="'docTool:ocrTextRecognition:batchDel'">{{$t('batchDelete')}}</a-button>
</div>
<div>
<j-table
:columns="columns"
:dataSource="dataSource"
:can-drag="true"
rowKey="id"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:pagination="ipagination"
:scroll="{x: '100%', y: yScrollHeight}"
:loading="loading"
:operation-list="operationList"
@change="handleTableChange"
@operationClick="operationClick">
<!--跳转标准详情-->
<template v-slot:tag="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text" v-if="text || text === 0"><a-tag :color="getTagColor(record)">{{ text }}</a-tag></div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</j-table>
</div>
<!-- 新增编辑弹框 -->
<ocr-text-recognition-modal ref="modalForm" @ok="modalFormOk"/>
</a-card>
</template>
<script>
import { JeroListMixin } from '@/mixins/JeroListMixin'
import JTable from '@comp/jero/JTable'
import OcrTextRecognitionModal from '@views/documentTool/ocrTextRecognition/modules/OcrTextRecognitionModal'
import { downloadFile, getAction, postAction } from '@api/manage'
export default {
name: 'OcrTextRecognition',
components: { OcrTextRecognitionModal, JTable },
mixins: [JeroListMixin],
data () {
return {
url: {
list: '/laws/ocr/OCRRestful/getAllOcrResult',
deleteBatch: '/laws/ocr/OCRRestful/deleteOcrRecord'
},
columns: [
{ // 序号
title: this.$t('serialNumber'),
key: 'rowIndex',
width: 80,
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
// { // 标准类别
// title: this.$t('docTool.ocrTextRecognition.standardCategory'),
// align: 'center',
// width: 180,
// dataIndex: 'fileType2',
// scopedSlots: { customRender: 'text' }
// },
{ // 标准编号
title: this.$t('docTool.ocrTextRecognition.standardNumber'),
align: 'center',
width: 180,
dataIndex: 'standNumber',
scopedSlots: { customRender: 'text' }
},
{ // 标准名称
title: this.$t('docTool.ocrTextRecognition.standardName'),
align: 'center',
width: 180,
dataIndex: 'standName',
scopedSlots: { customRender: 'text' }
},
{ // 文本状态
title: this.$t('docTool.ocrTextRecognition.textStatus'),
align: 'center',
width: 180,
dataIndex: 'fileType',
scopedSlots: { customRender: 'text' }
},
{ // 转换结果
title: this.$t('docTool.ocrTextRecognition.conversionResults'),
align: 'center',
width: 180,
dataIndex: 'resultContent',
scopedSlots: { customRender: 'tag' }
},
{ // 消耗时间
title: this.$t('docTool.ocrTextRecognition.timeConsuming'),
align: 'center',
width: 180,
dataIndex: 'spendTime',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('operation'),
scopedSlots: { customRender: 'action' },
align: 'center',
fixed: 'right',
width: 200
}
],
operationList: [
// 获取文件
{
text: this.$t('docTool.ocrTextRecognition.getFile'),
clickEvent: 'handleDownload',
has: 'docTool:ocrTextRecognition:download',
show: (record) => {
return record.resultContent !== '转换中'
}
},
// 编辑
{
text: this.$t('edit'),
clickEvent: 'handleEdit',
has: 'docTool:ocrTextRecognition:edit'
}
// 删除
// {
// text: this.$t('delete'),
// clickEvent: 'handleDelete',
// has: 'split:sarFileSplitInfo:release'
// }
]
}
},
mounted () {
console.log(this.$refs.modalForm)
},
methods: {
loadData (arg) {
if (!this.url.list) {
this.$message.warning('请设置url.list属性!')
return
}
// 加载数据 若传入参数1则加载第一页的内容
if (arg === 1) {
this.ipagination.current = 1
}
const params = this.getQueryParams()// 查询条件
console.log(params)
this.loading = true
postAction(this.url.list, params).then((res) => {
if (res.ok) {
this.dataSource = res.data.list
if (res.data.count) {
this.ipagination.total = res.data.count
} else {
this.ipagination.total = 0
}
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
handleDownload (data) {
downloadFile(`/sys/common/download/${data.wordAttId}`, data.docRealName)
},
getTagColor (row) {
if (row.resultContent === '转换成功') {
return 'green'
} else if (row.resultContent === '转换中') {
return 'blue'
} else {
return 'red'
}
},
batchDel: function () {
if (!this.url.deleteBatch) {
this.$message.warning('请设置url.deleteBatch属性!')
return
}
if (this.selectedRowKeys.length <= 0) {
this.$message.warning(this.$t('selectARecord'))
} else {
const ids = this.selectedRowKeys.join(',')
const that = this
this.$confirm({
title: this.$t('confirmBatchDeletion'),
content: this.$t('deleteAData'),
onOk: function () {
that.loading = true
getAction(that.url.deleteBatch, { ids: ids }).then((res) => {
if (res.ok) {
// 重新计算分页问题
that.reCalculatePage(that.selectedRowKeys.length)
that.$message.success(res.message)
that.loadData()
that.onClearSelected()
} else {
that.$message.warning(res.message)
}
}).finally(() => {
that.loading = false
})
}
})
}
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,176 @@
<template>
<j-modal
:title="title"
:width="800"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
:cancelText="$t('close')"
switchFullscreen
wrapClassName="ant-modal-cust-warp"
style="top:5%;overflow-y: hidden">
<a-spin :spinning="confirmLoading">
<a-form :form="form" :label-col="{ span: 5 }" :wrapper-col="{ span: 16 }">
<!--文件-->
<a-form-item label="文件">
<j-upload v-decorator="['attId', validatorRules.attId]"
@change="changeAttIdFile"
:number="1"
return-id
class="line-btn"
accept=".pdf"
file-type="pdf" />
</a-form-item>
<!--标准编号-->
<a-form-item :label="$t('docTool.ocrTextRecognition.standardNumber')">
<standard-selection :placeholder="$t('pleaseSelect') + $t('docTool.ocrTextRecognition.standardNumber')"
@nameChange="changeStandardName" type="radio"
v-decorator="['standNumber']" />
</a-form-item>
<!--标准名称-->
<a-form-item :label="$t('docTool.ocrTextRecognition.standardName')">
<a-input :placeholder="$t('pleaseEnter') + $t('docTool.ocrTextRecognition.standardName')"
class="box-input" :maxLength="500"
v-decorator="['standName']" />
</a-form-item>
<!--文本状态-->
<a-form-item :label="$t('docTool.ocrTextRecognition.textStatus')">
<a-select
:placeholder="$t('pleaseSelect') + $t('docTool.ocrTextRecognition.textStatus')"
v-decorator="['fileType', validatorRules.fileType]"
show-search
:filter-option="filterOption"
>
<a-select-option v-for="item in fileTypeList" :key="item.value" :value="item.value">{{ item.text || item.label }}</a-select-option>
</a-select>
</a-form-item>
</a-form>
</a-spin>
</j-modal>
</template>
<script>
import StandardSelection from '@comp/selection/StandardSelection'
import { getFileInfo } from '@api/api'
import { postAction } from '@api/manage'
import pick from 'lodash.pick'
export default {
name: 'OcrTextRecognitionModal',
components: { StandardSelection },
data () {
return {
form: this.$form.createForm(this),
title: '操作',
visible: false,
model: {},
url: {
add: '/laws/ocr/OCRRestful/addOcrRecord',
edit: '/laws/ocr/OCRRestful/updateOcrRecord'
},
// 文本状态下拉
fileTypeList: [
{ label: '修改单', value: '修改单' },
{ label: '草稿', value: '草稿' },
{ label: '报批稿', value: '报批稿' },
{ label: '发布稿(原文)', value: '发布稿(原文)' },
{ label: '相关文件', value: '相关文件' },
{ label: '关联文件', value: '关联文件' },
{ label: '征求意见稿', value: '征求意见稿' },
{ label: '发布稿(译文)', value: '发布稿(译文)' },
{ label: '送审稿', value: '送审稿' }
],
confirmLoading: false,
validatorRules: {
// 文件
attId: {
rules: [
{ required: true, trigger: 'change', message: this.$t('uploadFile.pleaseUpload') + this.$t('docTool.ocrTextRecognition.file') }
]
},
// 文本状态
fileType: {
rules: [
{ required: true, trigger: 'change', message: this.$t('pleaseSelect') + this.$t('docTool.ocrTextRecognition.textStatus') }
]
}
}
}
},
methods: {
add () {
this.edit()
},
edit (record) {
this.model = Object.assign({}, record)
console.log(record)
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'attId', 'fileType', 'standName', 'standNumber'))
})
},
close () {
this.form.resetFields()
this.$emit('close')
this.visible = false
},
handleOk () {
const that = this
this.form.validateFields((err, values) => {
if (!err) {
this.confirmLoading = true
const params = Object.assign({}, this.model, values)
const url = this.model.id ? this.url.edit : this.url.add
postAction(url, params).then((res) => {
if (res.success) {
that.$message.success(res.message)
that.close()
} else {
that.$message.warning(res.message)
}
}).finally(() => {
that.confirmLoading = false
})
}
})
},
handleCancel () {
this.close()
},
changeStandardName (value) {
this.form.setFieldsValue({
standName: value
})
},
filterOption (input, option) {
return (
option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
)
},
// 文件上传,通过id获取文件名
changeAttIdFile (fileId) {
this.getFileInfo(fileId).then(res => {
if (res && res.fileName) {
this.model.fileName = res.fileName
}
})
},
/**
* 通过文件的id获取文件的相关信息
* @param id
*/
getFileInfo (id) {
return getFileInfo({ id: id }).then(res => {
if (res.success) {
return res.result
}
})
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,871 @@
<template>
<a-card :bordered="false" class="page-left-right-layout">
<div class="page-left-box">
<a-input-search v-model="treeInput"
class="tree-search"
:placeholder="$t('pleaseEnterTreeSystem')"
@search="handleTreeSearch" />
<div class="page-tree-box page-tree-box-has-search">
<a-spin :spinning="treeLoading">
<a-tree
v-if="treeData.length > 0"
:show-line="true"
:show-icon="true"
:tree-data="treeData"
:expanded-keys.sync="expandedKeys"
:selectedKeys="currentTreeNode"
:default-expand-all="defaultExpandAll">
<template #title="record">
<div class="tree-operate-node"
@mouseenter="handleTreeMouseover(record.key)"
@mouseleave="handleTreeMouseout(record.key)"
@click="treeSelect(record)">
<a-icon class="parent-node-icon" type="folder" v-if="record.dataRef.children && record.dataRef.children.length > 0" />
<a-tooltip>
<template #title>
{{ record.title }}
</template>
<span class="tree-node-custom-title">{{ record.title }}</span>
</a-tooltip>
<div class="tree-operate-node-btn-box" :class="{'tree-operate-node-btn-box-hide': record.key !== mouseInNodeKey || record.key === 'myCollect'}">
<!-- 新增 -->
<a-button icon="plus" class="tree-operate-node-btn" v-has="'enterpriseStandard:tree:add'"
@click="handleTreeAdd(record)"></a-button>
<!-- 编辑 -->
<a-button v-if="record.nodeType !== StandardSystemTreeNodeType.ROOT_NODE.value"
v-has="'enterpriseStandard:tree:edit'"
class="tree-operate-node-btn" @click="handleTreeEdit(record)">
<i class="iconfont icon-bianji"></i>
</a-button>
<!-- 删除 -->
<a-button v-if="record.nodeType !== StandardSystemTreeNodeType.ROOT_NODE.value"
v-has="'enterpriseStandard:tree:delete'"
class="tree-operate-node-btn" @click="handleTreeDelete(record.id)">
<i class="iconfont icon-shanchu_"></i>
</a-button>
</div>
</div>
</template>
</a-tree>
</a-spin>
</div>
</div>
<div class="page-right-box">
<div class="table-page-search-wrapper">
<search ref="searchRef"
:flag="flag"
:get-query-condition-func="getQueryConditionFunc"
:default-search-params="defaultSearchParams"
search-has="enterpriseStandard:search"
@search="searchQuery"
@reset="searchReset" />
</div>
<div class="table-operator">
<!-- 新增 -->
<a-button icon="plus" type="primary" @click="handleAdd" v-has="'enterpriseStandard:add'">{{ $t('newlyAdded') }}</a-button>
<!-- 导入 -->
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl" @change="handleImportExcel">
<a-button icon="download" type="primary" ghost v-has="'enterpriseStandard:upload'">{{ $t('import') }}</a-button>
</a-upload>
<!-- 导出 -->
<a-button icon="upload" type="primary" ghost @click="handleExportXls($t('enterpriseStandardLibrary.standard.exportExcelFile'))" v-has="'enterpriseStandard:export'">{{ $t('export') }}</a-button>
<!-- 带文件导出 -->
<a-button icon="upload" type="primary" ghost @click="handleFileExport($t('enterpriseStandardLibrary.standard.exportZipFile'))" v-has="'enterpriseStandard:fileExport'">{{ $t('exportWithFile') }}</a-button>
<!-- 模板下载 -->
<a-button icon="download" type="primary" ghost @click="downTemplate($t('enterpriseStandardLibrary.standard.templateExport'))" v-has="'enterpriseStandard:templateDownload'">{{ $t('templateDownload') }}</a-button>
<!-- 批量删除 -->
<a-button icon="delete" type="primary" ghost @click="batchDel" v-has="'enterpriseStandard:batchDel'">{{ $t('batchDelete') }}</a-button>
<!-- 配置标准 -->
<a-button icon="setting" type="primary" ghost @click="handleAllocationStandard" v-has="'enterpriseStandard:allocationStandard'">{{ $t('enterpriseStandardLibrary.standard.allocationStandard') }}</a-button>
<!-- 移出标准 -->
<a-button icon="minus-circle" type="primary" ghost @click="handleShiftOutStandard" v-has="'enterpriseStandard:shiftOutStandard'">{{ $t('enterpriseStandardLibrary.standard.shiftOutStandard') }}</a-button>
<!-- 发起更改 -->
<a-button icon="setting" type="primary" ghost @click="handleInitChange" v-has="'enterpriseStandard:initChange'">{{ $t('enterpriseStandardLibrary.standard.initChange') }}</a-button>
<!-- 临时权限设置 -->
<a-button icon="setting" type="primary" ghost @click="handleTemporaryPermissSet" v-has="'enterpriseStandard:temporaryPermissSet'">{{ $t('enterpriseStandardLibrary.standard.temporaryPermissionSet') }}</a-button>
<!-- 临时权限记录 -->
<a-button icon="setting" type="primary" ghost @click="handleTemporaryPermissionRecord" v-has="'enterpriseStandard:temporaryPermissRecord'">{{ $t('enterpriseStandardLibrary.standard.temporaryPermissionRecord') }}</a-button>
</div>
<div>
<j-table
v-if="columns && columns.length > 0"
ref="tableRef"
:columns="columns"
:dataSource="dataSource"
:can-drag="true"
rowKey="id"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:pagination="ipagination"
:scroll="{x: '100%'}"
:loading="loading"
@change="tableOnChange">
<!-- 在混入里已经过滤过没有权限的按钮了 -->
<template slot="operation" slot-scope="{text, record}">
<!-- 有编辑删除权限 -->
<template v-if="record.editFlag">
<a v-for="(operation, index) in operationList.slice(0, operateMaxNum)" :key="index" @click="operationClick(operation,record)">
<template v-if="operation.text === $t('enterpriseStandardLibrary.standard.collection')">
<i class="iconfont icon-star1" v-show="record.collectionFlag" />
<i class="iconfont icon-star" v-show="!record.collectionFlag" />
</template>
</a>
<a-divider v-if="record.del_flag ? operationList.length > operateMaxNum : operationList.length - 1 > operateMaxNum" type="vertical" />
<a-dropdown v-if="record.del_flag ? operationList.length > operateMaxNum : operationList.length - 1 > operateMaxNum" placement="bottomCenter">
<a-icon type="more" class="operate-icon-btn operate-icon-more" />
<a-menu slot="overlay">
<!-- 有删除权限 -->
<template v-if="record.del_flag">
<a-menu-item v-for="(operation, index) in operationList.slice(operateMaxNum)" :key="index">
<a @click="operationClick(operation,record)">
<!-- 查看图标-->
<i class="iconfont icon-link operate-btn-icon" v-if="operation.text === $t('viewDetail')" />
<!-- 编辑图标 -->
<i class="iconfont icon-bianji operate-btn-icon" v-if="operation.text === $t('edit')" />
<!-- 删除图标 -->
<i class="iconfont icon-shanchu_ operate-btn-icon" v-if="operation.text === $t('delete')" />
{{ operation.text }}
</a>
</a-menu-item>
</template>
<!-- 没有删除权限 -->
<template v-else>
<a-menu-item v-for="(operation, index) in operationList.slice(operateMaxNum).filter(item => item.text !== $t('delete'))" :key="index">
<a @click="operationClick(operation,record)">
<!-- 查看图标-->
<i class="iconfont icon-link operate-btn-icon" v-if="operation.text === $t('viewDetail')" />
<!-- 编辑图标 -->
<i class="iconfont icon-bianji operate-btn-icon" v-if="operation.text === $t('edit')" />
{{ operation.text }}
</a>
</a-menu-item>
</template>
</a-menu>
</a-dropdown>
</template>
<!-- 没有编辑删除权限 -->
<template v-else>
<template v-if="operationList.find(item => item.text === $t('enterpriseStandardLibrary.standard.collection'))">
<a @click="operationClick(operationList.find(item => item.text === $t('enterpriseStandardLibrary.standard.collection')),record)">
<i class="iconfont icon-star1" v-show="record.collectionFlag" />
<i class="iconfont icon-star" v-show="!record.collectionFlag" />
</a>
</template>
<a-divider v-if="operationList.slice(operateMaxNum).filter(item => ![$t('edit'), $t('delete')].includes(item.text)).length > 0 &&
operationList.find(item => item.text === $t('enterpriseStandardLibrary.standard.collection'))"
type="vertical" />
<template v-if="operationList.slice(operateMaxNum).filter(item => ![$t('edit'), $t('delete')].includes(item.text)).length > 0">
<!--{{operationList.slice(operateMaxNum).filter(item => ![$t('edit'), $t('delete')].includes(item.text))}}-->
<template
v-if="operationList.slice(operateMaxNum).filter(item => ![$t('edit'), $t('delete')].includes(item.text)).length <= 1">
<template v-for="(operation, index) in operationList.slice(operateMaxNum).filter(item => ![$t('edit'), $t('delete')].includes(item.text))">
<a :key="index" @click="operationClick(operation,record)">
<!-- 查看图标-->
<i class="iconfont icon-link operate-btn-icon" v-if="operation.text === $t('viewDetail')" />
{{ operation.text }}
</a>
</template>
</template>
<template v-else>
<a-dropdown placement="bottomCenter">
<a-icon type="more" class="operate-icon-btn operate-icon-more" />
<a-menu slot="overlay">
<a-menu-item v-for="(operation, index) in operationList.slice(operateMaxNum).filter(item => ![$t('edit'), $t('delete')].includes(item.text))" :key="index">
<a @click="operationClick(operation,record)">
<!-- 查看图标-->
<i class="iconfont icon-link operate-btn-icon" v-if="operation.text === $t('viewDetail')" />
{{ operation.text }}
</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</template>
</template>
</template>
</template>
<!-- 企标编号 -->
<template v-slot:standard_number="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a class="link-a" @click="filePreview(record)" v-if="(text || text === 0) && record.detailFlag">{{ text }}</a>
<div class="table-text" v-else-if="(text || text === 0) && !record.detailFlag">{{ text }}</div>
<div class="table-text" v-else>{{ global.emptyLine }}</div>
</a-tooltip>
</template>
<!-- 企标名称 -->
<template v-slot:standard_name="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a class="link-a" @click="filePreview(record)" v-if="(text || text === 0) && record.detailFlag">{{ text }}</a>
<div class="table-text" v-else-if="(text || text === 0) && !record.detailFlag">{{ text }}</div>
<div class="table-text" v-else>{{ global.emptyLine }}</div>
</a-tooltip>
</template>
</j-table>
</div>
</div>
<!-- 新增、编辑弹框 -->
<enterprise-standard-modal ref="modalForm" @ok="modalFormOk"></enterprise-standard-modal>
<!-- 配置标准-标准选择弹框 -->
<allocation-standard-drawer ref="allocationStandardDrawer" @ok="modalOkDoNotRefreshTree"></allocation-standard-drawer>
<!-- 临时权限设置 -->
<temporary-permission-set ref="temporaryPermissionSet" @ok="modalFormOk"></temporary-permission-set>
<!-- 左侧树节点的新增和编辑 -->
<tree-node-modal ref="treeNodeModal" @ok="treeNodeModalOk"></tree-node-modal>
<!-- 临时权限记录 -->
<temporary-permission-record ref="temporaryPermissionRecord" @ok="modalFormOk"></temporary-permission-record>
</a-card>
</template>
<script>
import { StandardSystemTreeNodeType, EnterpriseStandardStatus } from '@/enums/commonEnums'
import JTable from '@/components/jero/JTable'
// 搜索组件
import Search from '@/components/customField/Search'
import { ConfigurableTableMixin } from '@/mixins/ConfigurableTableMixin'
import {
getTreeList,
getEnterpriseStandardTableHeader,
getEnterpriseStandardSearchForm,
shiftStandard,
collectStandard,
cancelCollectStandard,
deleteTreeNode,
getEnterpriseStandardInfoById,
getEnterpriseStandardAddForm
} from '@api/enterpriseStandardLibraryApi'
// 添加、编辑弹框
import EnterpriseStandardModal from './modules/EnterpriseStandardModal'
// 临时权限设置
import TemporaryPermissionSet from './modules/TemporaryPermissionSet'
import TreeNodeModal from './modules/TreeNodeModal'
// 配置标准弹框
import AllocationStandardDrawer from './modules/AllocationStandardDrawer'
import { postDownFile, downloadFile, postAction } from '@/api/manage'
import TemporaryPermissionRecord from './modules/TemporaryPermissionRecord'
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { previewPdf } from '@/utils/previewPdf'
import { kkFilePreview } from '@/utils/kkFilePreview'
import { getFileInfo } from '@api/api'
import { mapGetters } from 'vuex'
import { isHasPermission } from '@/utils/hasPermission'
export default {
name: 'EnterpriseStandardList',
components: { TemporaryPermissionRecord, AllocationStandardDrawer, TreeNodeModal, TemporaryPermissionSet, EnterpriseStandardModal, JTable, Search },
mixins: [ConfigurableTableMixin],
data () {
return {
StandardSystemTreeNodeType,
flag: '3',
treeInput: '', // 左侧树搜索框内容
treeData: [], // 左侧树数据
treeLoading: false,
collectionFlag: false,
defaultExpandAll: false, // 左侧树默认展开所有
currentTreeNode: null, // 当前选中的树节点key值
currentTreeNodeCode: null, // 当前选中的树节点的code值
mouseInNodeKey: null, // 鼠标悬浮的节点key值
operationList: [
{ // 收藏
text: this.$t('enterpriseStandardLibrary.standard.collection'),
clickEvent: 'handleCollection',
has: 'enterpriseStandard:collection'
},
// 查看
{
text: this.$t('viewDetail'),
clickEvent: 'detailClick',
has: 'enterpriseStandard:view'
},
{ // 编辑
text: this.$t('edit'),
clickEvent: 'handleEdit',
has: 'enterpriseStandard:edit'
},
{ // 删除
text: this.$t('delete'),
clickEvent: 'handleDelete',
has: 'enterpriseStandard:delete'
}
],
selectedRowKeys: [],
loading: false,
getTableHeaderFunc: getEnterpriseStandardTableHeader,
getQueryConditionFunc: getEnterpriseStandardSearchForm,
tableSlotField: ['standard_number', 'standard_name'], // 表格中需要插槽的字段
// url传参严格按照当前命名
url: {
list: '/laws/standard/enterprise/getCommonPage', // 表格数据
delete: '/laws/standard/enterprise/singleDelete', // 删除
deleteBatch: '/laws/standard/enterprise/deleteByIdsAndModule', // 批量删除
importUrl: '/laws/standard/enterprise/importExcelWithData', // 导入
exportXlsUrl: '/laws/standard/enterprise/exportData', // 导出
exportZipUrl: '/laws/standard/enterprise/exportDataWithFile', // 带文件导出
downTemplateUrl: '/laws/standard/enterprise/templateDownload' // 下载导入模板
},
showAction: true, // 是否显示操作列
expandedKeys: [], // 体系展开的节点
defaultSearchParams: {
standard_state: `${EnterpriseStandardStatus.CURRENTLY_EFFECTIVE.value},${EnterpriseStandardStatus.TRIAL_OPERATION.value},${EnterpriseStandardStatus.RELEASE.value},${EnterpriseStandardStatus.PRE_RELEASE.value}`
},
disabledMixinsCreate: true
}
},
computed: {
...mapGetters(['userInfo']),
// 当前用户是否管理员
isAdmin () {
const adminRoleList = window._CONFIG.adminRoleCode.split(',')
const userRoleList = this.userInfo.roleCode.split(',')
return userRoleList.some(tt => adminRoleList.includes(tt))
}
},
watch: {
// 消息当前页跳转,需要带入参数
$route: {
deep: true,
handler (to, from) {
// 如果有消息跳转参数就带入参数查询
if (to.query.standard_number) {
this.$set(this.$refs.searchRef.queryParam, 'standard_number', to.query.standard_number)
this.searchParams = Object.assign({}, this.$refs.searchRef.queryParam)
this.loadData()
}
}
}
},
mounted () {
// 从消息跳转进来带企业编号
this.$set(this.$refs.searchRef.queryParam, 'standard_number', this.$route.query.standard_number)
this.searchParams = Object.assign({}, this.$refs.searchRef.queryParam)
this.getTableHeader()
this.loadData()
// 过滤没有权限的按钮
if (this.needFilterTableBtn) {
this.operationList = this.operationList.filter(item => !item.has || isHasPermission(item.has))
console.log(this.operationList)
}
this.initTreeData()
},
methods: {
// 获取左侧树数据
initTreeData (params) {
this.treeLoading = true
getTreeList(params).then(res => {
this.treeData = []
this.currentTreeNode = []
this.currentTreeNodeCode = []
this.filters.nodeCode = null
if (res.success) {
this.treeData = res.result || []
this.expandedKeys = [this.treeData[0].key]
} else {
this.treeData = []
}
}).finally(() => {
this.$nextTick(() => {
this.defaultExpandAll = true
})
this.treeLoading = false
})
},
// 左侧树搜索
handleTreeSearch () {
this.treeData = []
this.initTreeData({ nodeName: this.treeInput })
},
// 左侧树点击选中
treeSelect ({ dataRef }) {
this.currentTreeNode = [dataRef.key]
this.currentTreeNodeCode = [dataRef.nodeCode]
this.filters.nodeCode = dataRef.nodeCode
this.loadData(1)
},
// 左侧树点击新增
handleTreeAdd (record) {
this.$refs.treeNodeModal.title = this.$t('newlyAdded')
this.$refs.treeNodeModal.add(record)
},
// 左侧树点击编辑
handleTreeEdit (record) {
console.log(record)
this.$refs.treeNodeModal.title = this.$t('edit')
this.$refs.treeNodeModal.edit(record)
},
// 左侧树点击删除
handleTreeDelete (id) {
const param = {}
param.id = id
this.$confirm({
title: this.$t('confirmDeletion'),
content: this.$t('confirmDeleteNodes'),
onOk: () => {
deleteTreeNode(param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.initTreeData()
} else {
this.$message.warning(res.message)
}
})
}
})
},
// 左侧树节点新增或编辑完成
treeNodeModalOk () {
this.initTreeData()
},
// 左侧树鼠标移入某一目录
handleTreeMouseover (key) {
this.mouseInNodeKey = key
},
// 左侧树鼠标移出某一目录
handleTreeMouseout (key) {
if (this.mouseInNodeKey === key) {
this.mouseInNodeKey = null
}
},
// 管理员直接新增企标到企标库,非管理员新增跳转到企标制修订发起页面
handleAdd () {
if (!this.isAdmin) {
this.$router.push({
path: '/workCenter/enterpriseStandardRevision/enterpriseStandardRevisionFlow'
})
return
}
this.$refs.modalForm.add()
this.$refs.modalForm.title = this.$t('newlyAdded')
this.$refs.modalForm.disableSubmit = false
},
/**
* 重置查询
*/
searchReset (params) {
this.currentTreeNode = null
this.searchParams = Object.assign({}, params)
this.selectedRowKeys = []
delete this.filters.nodeCode
this.getTableHeader()
this.loadData()
},
// 导出改成用post
handleExportXls (fileName) {
if (!fileName || typeof fileName !== 'string') {
fileName = '导出文件'
}
const param = this.getQueryParams()
if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
param.selections = this.selectedRowKeys.join(',')
}
console.log('导出参数', param)
// 加一个大的提示
const modalLoading = this.$info({
title: '提示',
content: <span>正在导出,请稍候 <a-spin size="small" /></span>,
keyboard: false
})
// 把知道了这个按钮去掉
this.$nextTick(() => {
document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none'
})
postDownFile(this.url.exportXlsUrl, param).then((data) => {
if (!data) {
this.$message.warning('文件下载失败')
return
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data], { type: 'application/vnd.ms-excel' }), fileName + '.xlsx')
} else {
const url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' }))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + '.xlsx')
document.body.appendChild(link)
link.click()
document.body.removeChild(link) // 下载完成移除元素
window.URL.revokeObjectURL(url) // 释放掉blob对象
}
}).finally(() => {
// 销毁这个提示
modalLoading.destroy()
})
},
// 带文件导出
handleFileExport (fileName) {
if (!fileName || typeof fileName !== 'string') {
fileName = '导出文件'
}
const param = this.getQueryParams()
if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
param.selections = this.selectedRowKeys.join(',')
}
console.log('导出参数', param)
// 加一个大的提示
const modalLoading = this.$info({
title: '提示',
content: <span>正在导出,请稍候 <a-spin size="small" /></span>,
keyboard: false
})
// 把知道了这个按钮去掉
this.$nextTick(() => {
document.getElementsByClassName('ant-modal-confirm-btns')[0].style = 'display:none'
})
postDownFile(this.url.exportZipUrl, param).then((data) => {
if (!data) {
this.$message.warning('文件下载失败')
}
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(new Blob([data]), fileName + '.zip')
} else {
const url = window.URL.createObjectURL(new Blob([data]))
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', fileName + '.zip')
document.body.appendChild(link)
link.click()
document.body.removeChild(link) // 下载完成移除元素
window.URL.revokeObjectURL(url) // 释放掉blob对象
}
// }
}).finally(() => {
// 销毁这个提示
modalLoading.destroy()
})
},
// 下载导入模板
downTemplate (fileName) {
if (!fileName || typeof fileName !== 'string') {
fileName = '模板文件'
}
downloadFile(this.url.downTemplateUrl, fileName + '.zip')
},
/**
* 批量删除
*/
batchDel () {
if (this.selectedRowKeys.length <= 0) {
this.$message.warning(this.$t('selectARecord'))
return
}
console.log(this.selectionRows)
if (this.selectionRows.find(item => !item.editFlag)) {
// 有没有查看权限的数据
this.$message.warning(this.$t('enterpriseStandardLibrary.standard.disabledBatchDel'))
return
}
const ids = this.selectedRowKeys.join(',')
this.$confirm({
title: this.$t('confirmBatchDeletion'),
content: this.$t('deleteAData'),
onOk: () => {
this.loading = true
postAction(this.url.deleteBatch, { ids: ids }).then((res) => {
if (res.success) {
// 重新计算分页问题
this.reCalculatePage(this.selectedRowKeys.length)
this.$message.success(res.message)
this.loadData()
this.onClearSelected()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
}
})
},
// 配置标准
handleAllocationStandard () {
console.log(this.currentTreeNode)
// 没选择左侧树节点,需要提示“请先选择二级及以下体系”
if (!this.currentTreeNode || this.currentTreeNode.length === 0 ||
this.currentTreeNode.includes('myCollect') || this.currentTreeNode.includes('adb15f66ce174f7295ab51fbc39c15b8')) {
this.$message.warn(this.$t('enterpriseStandardLibrary.standard.pleaseSelectLeastOneNode'))
return
}
this.$refs.allocationStandardDrawer.open(this.currentTreeNodeCode[0])
},
// 移出标准
handleShiftOutStandard () {
// 没选择左侧树节点,需要提示“请先选择左侧体系”
if (!this.currentTreeNode || this.currentTreeNode.length === 0 ||
this.currentTreeNode.includes('myCollect') || this.currentTreeNode.includes('adb15f66ce174f7295ab51fbc39c15b8')) {
this.$message.warn(this.$t('enterpriseStandardLibrary.standard.pleaseSelectLeastOneNode'))
return
}
// 左侧节点选的是我的收藏,不可移出标准
if (this.currentTreeNode[0] === 'myCollect') {
this.$message.warn(this.$t('enterpriseStandardLibrary.standard.myCollectNotRemove'))
return
}
// 没有选择列表数据,需要提示“选中至少一条列表数据”
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectAtLeastOne'))
return
}
this.loading = true
const params = {
nodeCode: this.currentTreeNodeCode[0],
ids: this.selectedRowKeys.join(',')
}
shiftStandard(params).then(res => {
if (res.success) {
this.$message.success(res.message)
// 判断当前删除的数据是否是最后一页的最后一条数据,如果是的话页码减一
if (this.ipagination.current > 1 && ((this.ipagination.current - 1) * this.ipagination.pageSize) + 1 === this.ipagination.total) {
this.ipagination.current -= 1
}
this.modalOkDoNotRefreshTree()
} else {
this.$message.warn(res.message)
}
}).finally(() => {
this.loading = false
})
},
// 发起更改
handleInitChange () {
// 没有选择列表数据,需要提示“请选择一条列表数据”
if (!this.selectedRowKeys || this.selectedRowKeys.length !== 1) {
this.$message.warning(this.$t('selectOne'))
return
}
this.$router.push({
path: '/workCenter/enterpriseStandardChange/enterpriseStandardChangeFlow',
query: {
enStandardId: this.selectedRowKeys[0]
}
})
},
// 临时权限设置
handleTemporaryPermissSet () {
// 没有选择列表数据,需要提示“选中至少一条列表数据”
if (!this.selectedRowKeys || this.selectedRowKeys.length === 0) {
this.$message.warning(this.$t('selectLeastOne'))
return
}
const notPremissData = this.selectionRows.filter(item => !item.detailFlag).map(item => item.standard_number)
if (notPremissData && notPremissData.length > 0) {
// 选中数据存在没有查看权限的数据
this.$message.warning(this.$t('enterpriseStandardLibrary.standard.youNot') +
notPremissData.join(',') + this.$t('enterpriseStandardLibrary.standard.dataPremissReCheck'))
return
}
this.$refs.temporaryPermissionSet.open(this.selectedRowKeys.join(','))
},
// 临时权限记录
handleTemporaryPermissionRecord () {
this.$refs.temporaryPermissionRecord.open()
},
// 操作栏的点击
operationClick (operation, record) {
if (operation.clickEvent === 'handleDelete') {
this.handleDelete(record.id)
} else {
this[operation.clickEvent](record)
}
},
// 收藏
handleCollection (record) {
if (this.collectionFlag || this.loading) {
return
}
this.collectionFlag = true
if (record.collectionFlag) {
const params = {}
params.id = record.id
// 说明已收藏,调取消收藏接口
cancelCollectStandard(params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loadData()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.collectionFlag = false
})
} else {
const params = {}
params.standardId = record.id
params.standardNumber = record.standard_number
collectStandard(params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loadData()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.collectionFlag = false
})
}
},
// 跳转详情
detailClick (record) {
this.$openPageNewSheet({
path: '/enterpriseStandardLibrary/enterpriseStandardDetail',
query: {
id: record.id
}
})
},
/**
* 按顺序打开最新的pdf或word
*/
async filePreview (record) {
console.log('filePreview', record)
// 获取并处理过程稿件数据
let res = await getEnterpriseStandardAddForm({ type: 1 })
if (!res.success) {
this.$message.warn(res.message)
console.log('getDomesticStandardFormModal error:', res)
return
}
console.log('res.result', res.result)
const data = res.result || {}
const form = data
const partList = Object.keys(data)
let part
const partZh = '过程稿件'
const partEn = 'Process Manuscript'
if (partList.includes(partEn))part = partEn
if (partList.includes(partZh))part = partZh
console.log('part', part)
const processManuscript = form[part]
console.log('过程稿件', processManuscript)
// 获取并处理详情数据
res = await getEnterpriseStandardInfoById({ id: record.id, type: 1 })
if (!res.success) {
this.$message.warn(res.message)
console.log('getDomesticStandardDetail error:', res)
return
}
const detailData = Object.assign({}, res.result || {})
console.log('detailData', detailData)
// 选择合适的文件
let file = null
const pdfFileList = []
const docFileList = []
let newestFileList = []
let manuscriptName = '' // 记录找到哪个稿件就找到文件了
// 按照过程稿件顺序寻找文件
for (const formItem of processManuscript) {
const keyId = formItem.dbFieldName
const keyName = formItem.dbFieldName + '_dictText'
if (!detailData[keyId]) continue
const idArray = detailData[keyId].split(',')
const nameArray = detailData[keyName].split(',')
// 每个过程中可能有多个文件
for (let i = 0; i < idArray.length; i++) {
const id = idArray[i]
const name = nameArray[i]
var suffix = name.substring(name.lastIndexOf('.') + 1).toLowerCase()
if (suffix === 'pdf') {
pdfFileList.push({ id, name, suffix })
}
if (suffix === 'docx') {
docFileList.push({ id, name, suffix })
}
}
// 一旦一个过程中找到pdf或者doc,就可以退出
if (pdfFileList.length > 0 || docFileList.length > 0) {
manuscriptName = formItem.dbFieldName
break
}
}
if (manuscriptName === 'publish_of_original') {
// 在发布稿就找到了,合并pdf和doc文件列表排序
newestFileList = [...pdfFileList, ...docFileList]
} else {
newestFileList = pdfFileList // 默认从pdf里选最新的文件
if (pdfFileList.length === 0)newestFileList = docFileList // 如果没有pdf文件,就从doc文件里选
}
file = await this.selectNewestFile(newestFileList)
if (file === null) { // 没有找到文件就退出
console.log('没有找到符合要求的文件!')
this.$message.error('该标准暂无预览文件,请联系标准管理员。')
return
}
console.log('seletedFile', file)
// 是onlyOffice生成的,预览关联的pdf文件
if (file.pdfId) {
// 说明是onlyOffice生成的
const url = previewPdf(file.pdfId)
window.open(url)
return
}
// 预览选择的文件
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.name}`
console.log(fileFullUrl)
if (file.suffix === 'pdf') { // pdf预览
const url = previewPdf(file.id)
window.open(url)
return
}
kkFilePreview(fileFullUrl)// 其余可预览文件仍使用KKFile进行预览
},
/**
* 从文件列表里选择最新文件
* @param fileList
*/
async selectNewestFile (fileList) {
if (!fileList || fileList.length === 0) return null
if (fileList.length === 1) {
const res = await getFileInfo(fileList[0])
return { ...fileList[0], ...res.result }
}
const newFileList = []
// 获取时间信息
for (const file of fileList) {
const res = await getFileInfo(file)
newFileList.push({ ...file, ...res.result })
}
// 排序取出最新
newFileList.sort((a, b) => b.id - a.id) // 日期只精确到天,用id更能区分
console.log('newFileList', newFileList)
return newFileList[0]
},
// 操作完需要刷树的回调
modalFormOk () {
this.selectedTreeKeys = []
this.filters.nodeCode = null
this.initTreeData()
// 新增/修改 成功时,重载列表
this.loadData()
// 清空列表选中
this.onClearSelected()
},
modalOkDoNotRefreshTree () {
// 新增/修改 成功时,重载列表
this.loadData()
// 清空列表选中
this.onClearSelected()
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
/deep/ .ant-tree li span.ant-tree-switcher.ant-tree-switcher-noop {
color: #1D2129;
}
.parent-node-icon {
color: #1D2129;
font-size: 16px;
margin-right: 10px;
}
</style>
@@ -0,0 +1,436 @@
<template>
<div class="detail-page">
<div class="detail-page-title">{{ $t('enterpriseStandardLibrary.standard.detailTitle') }}</div>
<div class="detail-page-part" v-for="(part, index) in partList" :key="part + index">
<div class="detail-page-part-title">
<span>{{ part }}</span>
<div class="detail-page-part-title-operate-box" v-if="index === 0">
<!-- 企标评价-->
<a-button type="link" @click="handleEvaluate" v-has="'enterpriseStandard:detail:evaluate'">
<i class="iconfont icon-edit-1" />
{{ $t('enterpriseStandardLibrary.standard.evaluate') }}
</a-button>
<!-- 更新记录-->
<a-button type="link" @click="handleOpenUpdateRecord" v-has="'enterpriseStandard:detail:updateLog'">
<i class="iconfont icon-root-list" />
{{ $t('enterpriseStandardLibrary.standard.updateRecord') }}
</a-button>
<!-- 提交问题-->
<a-button type="link" @click="submitQuestion" v-has="'enterpriseStandard:detail:submitQuestion'">
<i class="iconfont icon-chat" />
{{ $t('enterpriseStandardLibrary.standard.submitQuestion') }}
</a-button>
</div>
</div>
<div class="detail-page-part-form" v-if="part !== '过程稿件' && part !== 'Process Manuscript'">
<div class="detail-page-part-form-item" v-for="formItem in form[part]" :key="formItem.id">
<label>{{ formItem.dbFieldTxt }}</label>
<!--标准需要跳转是标准字段且有内容的情况下在这处理-->
<template v-if="standardFields.includes(formItem.dbFieldName) && !!detailData[formItem.dbFieldName]">
<div class="div-form-content">
<template v-for="(standard, standardIndex) in detailData[formItem.dbFieldName].split(',')">
<div class="can-click" :key="'standardKey' + standardIndex" @click="toStandardDetailPage(standard)">{{ standard }}</div>
<div v-if="standardIndex!==detailData[formItem.dbFieldName].split(',').length - 1">,</div>
</template>
</div>
</template>
<div v-else-if="fileField.includes(formItem.dbFieldName)" class="file-box">
<template v-if="detailData[formItem.dbFieldName]">
<div class="detail-page-process-manuscript-file" v-for="file in detailData[formItem.dbFieldName + 'List']" :key="file.id">
<div class="detail-page-process-manuscript-file-info">
<a-icon type="link" />
<div class="detail-page-process-manuscript-file-name" @click="handlePreview(file)">{{ file.fileName }}</div>
</div>
<!-- 下载-->
<a-button type="link" icon="download" class="file-btn" @click="handleDownload(file)">
{{ $t('download') }}
</a-button>
<!-- 无水印下载-->
<!--<a-button type="link" v-has="btnPermission.unwatermarkedDownload" class="file-btn">-->
<!-- <i class="iconfont icon-water-drop-slash"></i>-->
<!-- {{ $t('unwatermarkedDownload') }}-->
<!--</a-button>-->
</div>
</template>
<template v-else>
<span></span>
</template>
</div>
<!-- 需要显示查看全部按钮 -->
<a-button v-else-if="showViewAllField.includes(formItem.dbFieldName) &&
(detailData[formItem.dbFieldName + '_dictText'] || detailData[formItem.dbFieldName])"
type="primary" ghost @click="viewAll(formItem.dbFieldName)">
{{ $t('enterpriseStandardLibrary.standard.viewAll')}}
</a-button>
<span v-else-if="detailData[formItem.dbFieldName + '_dictText']">{{ detailData[formItem.dbFieldName + '_dictText'] }}</span>
<!-- 标准适用范围,有标签需要编译 -->
<span v-else-if="detailData[formItem.dbFieldName] && formItem.dbFieldName === 'standard_scope'" v-html="detailData[formItem.dbFieldName]"></span>
<span v-else-if="detailData[formItem.dbFieldName]">{{ detailData[formItem.dbFieldName] }}</span>
<span v-else>—</span>
</div>
</div>
<!-- 过程稿件特殊处理-->
<div class="detail-page-process-manuscript" v-if="part === '过程稿件' || part === 'Process Manuscript'">
<div class="detail-page-process-manuscript-item" v-for="(formItem, formIndex) in form[part]" :key="formItem.id">
<div class="detail-page-process-manuscript-title">
<span>{{ formItem.dbFieldTxt }}</span>
<a-button v-if="formIndex === 0"
type="primary"
ghost
icon="file-text"
v-has="'enterpriseStandard:detail:standardResolutionSheet'"
@click="previewStandardResolutionSheet(detailData[formItem.dbFieldName + '_standardSplit'])"
:disabled="!detailData[formItem.dbFieldName + '_standardSplit']">
{{ $t('enterpriseStandardLibrary.standard.standardResolutionSheet') }}
</a-button>
</div>
<template v-if="detailData[formItem.dbFieldName]">
<div class="detail-page-process-manuscript-file" v-for="file in detailData[formItem.dbFieldName + 'List']" :key="file.id">
<div class="detail-page-process-manuscript-file-info">
<a-icon type="link" />
<div class="detail-page-process-manuscript-file-name" @click="handlePreview(file)">{{ file.fileName }}</div>
</div>
<div class="detail-page-process-manuscript-file-operate">
<!-- 下载-->
<a-button type="link" icon="download" @click="handleDownload(file)" v-has="'enterpriseStandard:detail:download'">
{{ $t('download') }}
</a-button>
<!-- 无水印下载-->
<a-button type="link" @click="downloadDraftNoWater(file)" v-has="'enterpriseStandard:detail:noWaterDownload'">
<i class="iconfont icon-water-drop-slash"></i>
{{ $t('unwatermarkedDownload') }}
</a-button>
</div>
</div>
</template>
</div>
</div>
</div>
<!-- 稽查内容 -->
<div class="detail-page-part">
<div class="detail-page-part-title">
<span>稽查内容</span>
</div>
<div class="detail-page-part-table">
<a-table
:scroll="{x: 500}"
:columns="columns"
:dataSource="dataSource"
:pagination="false"
bordered
rowKey="id">
<template slot="text" slot-scope="text">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text">{{ text || text === 0 ? text : global.emptyLine }}</div>
</a-tooltip>
</template>
</a-table>
</div>
</div>
<!-- 企标评价 -->
<enterprise-standard-evaluate-modal ref="enterpriseStandardEvaluateModal"></enterprise-standard-evaluate-modal>
<!-- 更新记录弹框 -->
<update-record-modal ref="updateRecordModal"></update-record-modal>
<!-- 提交问题弹框 -->
<submit-question-modal ref="submitQuestionModal"></submit-question-modal>
<!-- 标准分解单-->
<standard-resolution-sheet-modal ref="standardResolutionSheetModal" />
<!-- 图片预览容器 -->
<div id="images">
<div class="image" v-viewer="{movable: false}">
<img v-show="image" :src="imageUrl">
</div>
</div>
<text-content-modal ref="textContentModal"></text-content-modal>
</div>
</template>
<script>
import '@assets/less/common.less'
import { FieldType, StandardSource } from '@/enums/commonEnums'
import { downloadFile, getFileAccessHttpUrl } from '@/api/manage'
import { getEnterpriseStandardInfoById, getEnterpriseStandardAddForm } from '@/api/enterpriseStandardLibraryApi'
import UpdateRecordModal from './module/UpdateRecordModal'
import SubmitQuestionModal from './module/SubmitQuestionModal'
import EnterpriseStandardEvaluateModal from './module/EnterpriseStandardEvaluateModal'
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
import { previewPdf } from '@/utils/previewPdf'
import { getFileInfo } from '../../../../api/api'
import UserSelectModal from '@comp/selection/UserSelectModal'
import StandardResolutionSheetModal from '@views/standardRegulationLibrary/commonPage/modules/StandardResolutionSheetModal'
import { kkFilePreview } from '@/utils/kkFilePreview'
import TextContentModal from './module/TextContentModal'
import { queryStandardInfoByStandardNumber } from '@/utils/util'
// 支持预览的文件后缀
const CAN_PREVIEW_FILE_SUFFIX = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
const FILE_TYPE_IMGS = ['jpg', 'jpeg', 'png', 'raw']
const FILE_TYPE_PDF = 'pdf'
export default {
name: 'EnterpriseStandardDetail',
components: { TextContentModal, StandardResolutionSheetModal, UserSelectModal, EnterpriseStandardEvaluateModal, SubmitQuestionModal, UpdateRecordModal },
data () {
return {
FieldType,
partList: [], // 页面模块列表
form: {}, // 页面字段
detailData: {}, // 页面数据
fileField: ['comp_illustration', 'associated_file', 'publish_of_original', 'modification_list', 'draft_for_review', 'draft_for_comment', 'draft'], // 是文件的字段
columns: [
{
title: this.$t('serialNumber'),
dataIndex: '',
key: 'rowIndex',
width: 100,
align: 'center',
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
// 章节条款
{
title: this.$t('enterpriseStandardLibrary.standard.subjectToClause'),
align: 'center',
width: 200,
dataIndex: 'clause',
scopedSlots: { customRender: 'text' }
},
// 标准内容或要求
{
title: this.$t('enterpriseStandardLibrary.standard.standardContentOrRequirements'),
align: 'center',
dataIndex: 'requirement',
scopedSlots: { customRender: 'text' }
},
// 整改部门
{
title: this.$t('enterpriseStandardLibrary.standard.rectificationDepartment'),
align: 'center',
dataIndex: 'rectificationDepartment_dictText',
width: 400,
scopedSlots: { customRender: 'text' }
}
], // 稽查内容表格列参数
dataSource: [], // 稽查内容数据
image: false,
imageUrl: '',
showViewAllField: ['auth_dept'], // 需要显示展示查看全部按钮的字段
// 标准字段,需要跳转标准详情
standardFields: ['substitute_standard_number', 'replaced_by_standard_number', 'reference_standard', 'referenced_standard']
}
},
created () {
this.initForm()
this.initData()
},
methods: {
initForm () {
const params = {}
params.type = 1
getEnterpriseStandardAddForm(params).then(res => {
if (res.success) {
const data = res.result || {}
this.partList = Object.keys(data)
this.form = data
} else {
this.$message.warn(res.message)
}
})
},
initData () {
const params = {}
if (this.$route.query.id) {
params.id = this.$route.query.id
} else if (this.$route.query.standardNumber) {
params.standardNumber = this.$route.query.standardNumber
}
params.type = 1
getEnterpriseStandardInfoById(params).then(res => {
if (res.success) {
this.detailData = res.result
this.dataSource = this.detailData.checkList
this.fileField.forEach(item => {
if (this.detailData[item]) {
const fileList = this.detailData[item].split(',')
const fileInfoList = []
fileList.forEach(async id => {
const fileInfo = await this.getFileInfo(id)
if (fileInfo) {
fileInfoList.push(fileInfo)
}
})
this.$set(this.detailData, item + 'List', fileInfoList)
}
})
} else {
this.$message.warning(res.message)
}
})
},
getFileInfo (fileId) {
return new Promise(resolve => {
let fileInfo
getFileInfo({ id: fileId }).then(res => {
if (res.success) {
fileInfo = res.result
}
}).finally(() => {
resolve(fileInfo)
})
})
},
handlePreview (file) {
if (!file || !file.url) {
return
}
if (file.pdfId) {
// 说明是onlyOffice生成的
const url = previewPdf(file.pdfId)
window.open(url)
return
}
// 截取文件后缀名
const fileSuffix = file.fileName ? file.fileName.split('.')[file.fileName.split('.').length - 1] : ''
const canPreview = CAN_PREVIEW_FILE_SUFFIX.some(tt => fileSuffix.toLowerCase() === tt)
// 判断是否为可预览格式的文件
if (!canPreview) {
this.$message.loading('该文件类型不支持预览,正在为您准备下载...').then(() => {
this.handleDownload(file)
})
return
}
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.fileName}`
// 图片预览,使用自己添加的组件
if (canPreview && FILE_TYPE_IMGS.includes(fileSuffix.toLowerCase())) {
this.imageUrl = getFileAccessHttpUrl(file.id)
// 获取viewer实例
const viewer = this.$el.querySelector('.image').$viewer
// 调用show方法进行显示预览图
viewer.show()
// this.$refs.imagePreviewModal.open(file)
return
}
// pdf预览
if (canPreview && FILE_TYPE_PDF.includes(fileSuffix.toLowerCase())) {
const url = previewPdf(file.id)
console.log(url)
window.open(url)
return
}
// 其余可预览文件仍使用KKFile进行预览
kkFilePreview(fileFullUrl)
},
handleDownload (file) {
// 下载文件
downloadFile(`/sys/common/devEncryptDownload/${file.id}`, file.fileName)
},
// 过程稿件里的无水印下载
downloadDraftNoWater (file) {
downloadFile(`/sys/common/devEncryptDownloadWithoutWaterMark/${file.id}`, file.fileName)
},
// 企标评价
handleEvaluate () {
this.$refs.enterpriseStandardEvaluateModal.open(this.detailData.id)
},
// 更新记录
handleOpenUpdateRecord () {
this.$refs.updateRecordModal.open(this.detailData.standard_number)
},
// 提交问题
submitQuestion () {
this.$refs.submitQuestionModal.open(this.detailData.standard_number)
},
/**
* 查看标准分解单
*/
previewStandardResolutionSheet (id) {
this.$refs.standardResolutionSheetModal.open(id)
},
// 查看全部
viewAll (fieldName) {
const data = this.detailData[fieldName + '_dictText'] || this.detailData[fieldName]
this.$refs.textContentModal.open(data)
},
async toStandardDetailPage (standardNumber) {
const standardInfo = await queryStandardInfoByStandardNumber(standardNumber)
if (!standardInfo) {
return
}
let path
// 根据来源跳转不同的详情页
switch (standardInfo.source) {
// 国内
case StandardSource.DOMESTIC.value:
path = '/standardRegulationLibrary/DomesticStandardDetail'
break
// 海外
case StandardSource.OVERSEAS.value:
path = '/standardRegulationLibrary/OverseasStandardDetail'
break
// 企标
case StandardSource.ENTERPRISE.value:
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
break
default:
break
}
if (!path) {
return
}
const query = {
id: standardInfo.id
}
this.$openPageNewSheet({
path, query
})
}
}
}
</script>
<style scoped lang="less">
// 单独设置此页面一行三个的label
.detail-page-part-form-item label {
width: 8.5em;
}
.detail-page-part-table {
margin: 20px;
}
.file-box {
flex: 1;
width: 0;
}
.file-btn > ::v-deep span {
display: inline;
color: @primary-color;
font-size: 14px;
}
.div-form-content {
word-break: break-all;
white-space: pre-wrap;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
color: #1D2129;
-ms-flex: 1;
flex: 1;
width: 0;
}
.div-form-content > div {
display: inline-block;
}
.can-click {
display: inline-block;
color: @primary-color;
cursor: pointer;
}
</style>
@@ -0,0 +1,403 @@
<template>
<j-modal
:title="title"
:maskClosable="true"
:width="1000"
:closable="true"
switchFullscreen
@cancel="handleCancel"
:footer="false"
:visible="visible">
<a-spin :spinning="confirmLoading">
<div class="content-wrapper">
<div class="left-wrapper">
<div v-for="(item, index) in nameArr" :key="item" class="name-div">
<a-button class="left-btn" :type="leftCurr === item ? 'primary' : ''" :disabled="index === 0" @click="leftChange(item)">{{ item }}</a-button>
</div>
</div>
<div class="right-wrapper">
<a-table
bordered
size="middle"
:scroll="{x: '100%'}"
:columns="columns"
:dataSource="dataSource"
rowKey="id"
:pagination="false"
>
<template slot="text" slot-scope="text">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div>{{ text || text === 0 ? text : global.emptyLine }}</div>
</a-tooltip>
</template>
</a-table>
</div>
</div>
</a-spin>
</j-modal>
</template>
<script>
import { detailEvaluate } from '@/api/enterpriseStandardLibraryApi'
const fixDataSourse = [
{
id: 1,
project: '文本格式(15)',
desc: '评估标准文件的格式是否符合GB/T 1.1的要求,包括文件名、编号、版本号、' +
'发布日期、生效日期、引用文件章节、图表等内容是否齐全、准确、清晰。',
score: '一般',
scoreRange: '0~5'
},
{
id: 2,
score: '优秀',
scoreRange: '6~10'
},
{
id: 3,
score: '优秀',
scoreRange: '11~15'
},
{
id: 4,
project: '标准内容(45)',
desc: '评估标准文件的内容是否详细准确、内容详实、逻辑清晰、科学合理。包括术语定义、' +
'引用标准、技术要求、试验检测方法等各个方面是否符合/严于法律法规、国家标准、行业标准等要求。',
score: '一般',
scoreRange: '0~20'
},
{
id: 5,
score: '优秀',
scoreRange: '21~35'
},
{
id: 6,
score: '优秀',
scoreRange: '36~45'
},
{
id: 7,
project: '适用性 (10)',
desc: '评估标准文件的应用范围是否明确、适用,是否与研发工艺、质量、生产、采购等环节相符合。',
score: '一般',
scoreRange: '0~3'
},
{
id: 8,
score: '优秀',
scoreRange: '4~6'
},
{
id: 9,
score: '优秀',
scoreRange: '7~10'
},
{
id: 10,
project: '完整性 (15)',
desc: '评估标准文件所涉及的专业领域是否全面、系统,是否涵盖了所有相关模版' +
'(比如设计规范模版、释放规范模版、产品技术条件模版)所规定的要素和环节。',
score: '一般',
scoreRange: '0~5'
},
{
id: 11,
score: '优秀',
scoreRange: '6~10'
},
{
id: 12,
score: '优秀',
scoreRange: '11~15'
},
{
id: 13,
project: '可读性 (5)',
desc: '评估标准文件的可读性是否良好,是否有错别字,是否易于阅读和理解。',
score: '一般',
scoreRange: '0~2'
},
{
id: 14,
score: '优秀',
scoreRange: '3~4'
},
{
id: 15,
score: '优秀',
scoreRange: '5'
},
{
id: 16,
project: '权威性及可执行性(10)',
desc: '评估标准文件的权威性是否足够,是否得到了相关方的认可和信任,是否得到了有效的贯彻和执行。',
score: '一般',
scoreRange: '0~3'
},
{
id: 17,
score: '优秀',
scoreRange: '4~6'
},
{
id: 18,
score: '优秀',
scoreRange: '7~10'
},
{
id: 19,
project: '总分:0'
}
]
export default {
name: 'EnterpriseStandardEvaluateModal',
data () {
return {
title: this.$t('enterpriseStandardLibrary.reviewMeeting.scoreDetailTitle'),
visible: false,
confirmLoading: false,
scoreData: {},
nameArr: [],
leftCurr: '',
columns: [
{
title: this.$t('enterpriseStandardLibrary.reviewMeeting.project'),
align: 'center',
width: 100,
dataIndex: 'project',
customRender: (text, row, index) => {
const obj = {
attrs: {}
}
if (index % 3 === 0) {
obj.children = text || this.global.emptyLine
obj.attrs.rowSpan = 3
} else if (index % 3 !== 0 && index !== 18) {
obj.children = text || this.global.emptyLine
obj.attrs.rowSpan = 0
}
if (index === 18) {
obj.children = <p class="all-score">总分: {this.allScore}</p>
obj.attrs.colSpan = 6
}
return obj
}
},
{
title: this.$t('enterpriseStandardLibrary.reviewMeeting.desc'),
align: 'center',
// width: 100,
dataIndex: 'desc',
customRender: (text, row, index) => {
const obj = {
children: text || this.global.emptyLine,
attrs: {}
}
if (index % 3 === 0) {
obj.attrs.rowSpan = 3
} else if (index % 3 !== 0 && index !== 18) {
obj.attrs.rowSpan = 0
}
if (index === 18) {
obj.attrs.colSpan = 0
}
return obj
}
},
{
title: this.$t('enterpriseStandardLibrary.reviewMeeting.score'),
align: 'center',
width: 100,
dataIndex: 'score',
colSpan: 2,
customRender: (text, row, index) => {
const obj = {
children: text || this.global.emptyLine,
attrs: {}
}
if (index === 18) {
obj.attrs.colSpan = 0
}
return obj
}
},
{
title: this.$t('enterpriseStandardLibrary.reviewMeeting.score'),
align: 'center',
width: 100,
dataIndex: 'scoreRange',
colSpan: 0,
customRender: (text, row, index) => {
const obj = {
children: text || this.global.emptyLine,
attrs: {}
}
if (index === 18) {
obj.attrs.colSpan = 0
}
return obj
}
},
{
title: this.$t('enterpriseStandardLibrary.reviewMeeting.grade'),
align: 'center',
width: 100,
dataIndex: 'evaluateScore',
customRender: (text, row, index) => {
const obj = {
children: text || this.global.emptyLine,
attrs: {}
}
if (index === 18) {
obj.attrs.colSpan = 0
}
if (index % 3 === 0) {
obj.attrs.rowSpan = 3
} else {
obj.attrs.rowSpan = 0
}
return obj
}
},
{
title: this.$t('enterpriseStandardLibrary.reviewMeeting.groundsForDemerit'),
align: 'center',
width: 100,
dataIndex: 'deductionScoreReason',
customRender: (text, row, index) => {
const obj = {
children: text || this.global.emptyLine,
attrs: {}
}
if (index === 18) {
obj.attrs.colSpan = 0
}
if (index % 3 === 0) {
obj.attrs.rowSpan = 3
} else {
obj.attrs.rowSpan = 0
}
return obj
}
}
],
dataSource: [],
allScore: 0
}
},
watch: {
leftCurr: {
immediate: true,
handler (val) {
console.log('leftCurr改变')
if (val) {
// 左侧选中改变,右侧表格数据也随之改变
const dataArr = this.scoreData[val]
if (dataArr) {
for (const i in fixDataSourse) {
this.dataSource[i] = { ...fixDataSourse[i] }
if (i % 3 === 0 && Number(i) !== 18) {
this.dataSource[i].evaluateScore = dataArr[Math.floor(i / 3)].evaluateScore || ''
this.dataSource[i].deductionScoreReason = dataArr[Math.floor(i / 3)].deductionScoreReason || ''
}
}
} else {
this.dataSource = [...fixDataSourse]
}
this.getAllScore()
}
}
}
},
methods: {
open (standardId) {
this.visible = true
this.initData(standardId)
},
// 获取总分
getAllScore () {
const arr = [0, 3, 6, 9, 12, 15]
let score = 0
for (const item of arr) {
score += Number(this.dataSource[item].evaluateScore) || 0
}
this.allScore = score
},
initData (standardId) {
this.confirmLoading = true
detailEvaluate(standardId).then(res => {
if (res.success) {
this.scoreData = res.result
this.nameArr = Object.keys(this.scoreData)
if (this.nameArr && this.nameArr.length > 0) {
// 第一条显示平均分:XX分
this.nameArr[0] = this.$t('enterpriseStandardLibrary.reviewMeeting.reviewMeeting') + this.nameArr[0] +
this.$t('enterpriseStandardLibrary.reviewMeeting.number')
}
this.leftCurr = this.nameArr[1]
} else {
this.$message.warning(res.message)
this.scoreData = []
this.nameArr = []
this.leftCurr = ''
}
}).finally(() => {
this.confirmLoading = false
})
},
// 左侧选中改变
leftChange (value) {
this.leftCurr = value
},
// 通过
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
.content-wrapper {
display: flex;
.left-wrapper {
width: 20%;
display: flex;
flex-direction: column;
align-items: center;
.name-div {
width: calc(100% - 20px);
margin-right: 20px;
text-align: center;
}
.left-btn {
margin-bottom: 10px;
max-width: 100%;
/deep/ span {
width: 100%;
vertical-align: middle;
//display: inline-block;
overflow-x: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
.right-wrapper {
width: 80%;
}
}
/deep/ .ant-table-body {
td {
padding: 8px !important;
}
}
</style>
@@ -0,0 +1,189 @@
<template>
<j-modal
:title="$t('standardRegulationLibrary.submitQuestion')"
:maskClosable="false"
:width="width"
:visible="visible"
switchFullscreen
:confirm-loading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel">
<a-form :form="form">
<a-row :gutter="24">
<a-col :span="12">
<!-- 标题-->
<a-form-item :label="$t('title')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter') + $t('title')"
:maxLength="50"
v-decorator="['title', validatorRules.title]" />
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 项目-->
<a-form-item :label="$t('project')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select :placeholder="$t('pleaseSelect') + $t('project')"
v-decorator="['project', validatorRules.project]"
show-search
:filter-option="filterOption">
<a-select-option v-for="item in projectSelectOptions" :key="item.id" :title="item.projectName">{{ item.projectName }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 标准编号-->
<a-form-item :label="$t('standardRegulationLibrary.standardDetail.standardNumber')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter') + $t('standardRegulationLibrary.standardDetail.standardNumber')"
:maxLength="50"
disabled
v-decorator="['standardNumber', validatorRules.standardNumber]" />
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 提问时间-->
<a-form-item :label="$t('standardRegulationLibrary.standardDetail.problemOccurrenceTime')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-date-picker :placeholder="$t('pleaseSelect') + $t('standardRegulationLibrary.standardDetail.problemOccurrenceTime')"
:getCalendarContainer="(trigger) => trigger.parentNode"
v-decorator="['quizTime', validatorRules.quizTime]"
showTime
style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<!-- 问题描述-->
<a-form-item :label="$t('standardRegulationLibrary.standardDetail.problemDescription')"
:labelCol="longLabelCol"
:wrapperCol="longWrapperCol">
<a-input :placeholder="$t('pleaseEnter') + $t('standardRegulationLibrary.standardDetail.problemDescription')"
type="textarea"
:maxLength="500"
v-decorator="['problemDescription', validatorRules.problemDescription]" />
<j-upload v-decorator="['file', validatorRules.file]" return-id multiple />
</a-form-item>
</a-col>
</a-row>
</a-form>
</j-modal>
</template>
<script>
import { detailSubmitQuestion } from '@/api/enterpriseStandardLibraryApi'
import { getCarTypeProjectList } from '@/api/projectLibraryApi.js'
import pick from 'lodash.pick'
import moment from 'moment'
export default {
name: 'SubmitQuestionModal',
data () {
return {
width: 800,
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
modal: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
longLabelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
longWrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
},
validatorRules: {
// 标题
title: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('title') }],
validateTrigger: 'blur'
},
// 项目
project: {
rules: [{ required: false, message: this.$t('pleaseSelect') + this.$t('project') }],
validateTrigger: 'change'
},
// 标准编号
standardNumber: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('standardRegulationLibrary.standardDetail.standardNumber') }],
validateTrigger: 'blur'
},
// 问题发生时间
quizTime: {
rules: [{ required: false, message: this.$t('pleaseSelect') + this.$t('standardRegulationLibrary.standardDetail.problemOccurrenceTime') }],
validateTrigger: 'change'
},
// 问题描述
problemDescription: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('standardRegulationLibrary.standardDetail.problemDescription') }],
validateTrigger: 'blur'
},
// 附件
file: {}
},
projectSelectOptions: [] // 项目下拉数据
}
},
mounted () {
this.initProjectOptions()
},
methods: {
open (standardNumber) {
this.visible = true
const param = {}
param.standardNumber = standardNumber
param.quizTime = moment().format('YYYY-MM-DD HH:mm:ss')
this.$nextTick(() => {
this.form.setFieldsValue(pick(param, 'standardNumber', 'quizTime'))
})
},
// 获取项目下拉框数据
initProjectOptions () {
getCarTypeProjectList({ column: 'createTime', order: 'desc' }).then(res => {
if (res.success) {
this.projectSelectOptions = res.result
} else {
this.$message.warning(res.message)
}
})
},
handleOk () {
if (this.confirmLoading) return
this.form.validateFields((err, values) => {
if (!err) {
this.confirmLoading = true
const param = Object.assign({}, values)
detailSubmitQuestion(param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
},
filterOption (input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,42 @@
<template>
<j-modal
:title="$t('view')"
:visible="visible"
:width="1000"
:footer="false"
switchFullscreen
@cancel="handleCancel">
<div class="text-content">{{ content }}</div>
</j-modal>
</template>
<script>
export default {
name: 'TextContentModal',
data () {
return {
visible: false,
content: null
}
},
methods: {
open (text) {
this.content = text
this.visible = true
},
handleCancel () {
this.visible = false
this.content = null
}
}
}
</script>
<style scoped lang="less">
.text-content {
max-height: calc(100vh - 200px - 55px);
overflow: auto;
margin: 0 -24px;
padding: 0 24px;
}
</style>
@@ -0,0 +1,76 @@
<template>
<j-modal
:title="$t('enterpriseStandardLibrary.standard.updateRecord')"
:maskClosable="false"
:width="width"
:visible="visible"
:confirm-loading="confirmLoading"
switchFullscreen
@cancel="handleCancel">
<a-spin :spinning="confirmLoading">
<template v-if="recordList && recordList.length > 0">
<div class="record-item" v-for="record in recordList" :key="record.id">
{{ record.content }}
</div>
</template>
<a-empty v-else />
</a-spin>
<template v-slot:footer>
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import { detailGetUpdateRecord } from '@/api/enterpriseStandardLibraryApi'
export default {
name: 'UpdateRecordModal',
data () {
return {
width: 800,
visible: false,
confirmLoading: false,
recordList: []
}
},
methods: {
open (standardNumber) {
this.visible = true
this.initRecord(standardNumber)
},
initRecord (standardNumber) {
this.confirmLoading = true
detailGetUpdateRecord({ standardNumber }).then(res => {
if (res.success) {
this.recordList = res.result || []
} else {
this.$message.warn(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
},
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
.record-item {
padding: 20px 0;
border-bottom: 1px solid #E5E6EB;
}
.record-item:first-child {
padding-top: 0;
}
.record-item:last-child {
border-bottom: none;
}
</style>
@@ -0,0 +1,228 @@
<template>
<a-drawer
:title="$t('standardSelect.standardSelection')"
:maskClosable="false"
:width="1000"
placement="right"
:closable="true"
@close="handleCancel"
:visible="visible"
class="custom-drawer-style">
<div class="custom-drawer-style-scroll">
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :sm="8">
<a-form-item :label="$t('standardSelect.standardNumOrName')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter')+$t('standardSelect.standardNumOrName')"
v-model="queryParam.standardNumberOrName"></a-input>
</a-form-item>
</a-col>
<div style="float: right;overflow: hidden;margin-right: 41px;margin-bottom: 20px" class="table-page-search-submitButtons">
<a-button class="box-button" style="margin-left: 8px" @click="searchReset">{{ $t('reset') }}</a-button>
<a-button class="box-button" style="margin-left: 8px" type="primary" @click="searchQuery">{{ $t('query') }}</a-button>
</div>
</a-row>
</a-form>
</div>
<a-table
:columns="columns"
rowKey="id"
:scroll="{x: 900}"
:data-source="dataList"
:pagination="ipagination"
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
:loading="loading"
@change="handleTableChange">
<!-- 标准编号标准名称跳转详情-->
<template v-slot:detail="text, record">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text can-click-table-text" v-if="text || text === 0" @click="toDetailPage(record)">{{ text }}</div>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
<template slot="text" slot-scope="text">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text">{{ text || text === 0 ? text : global.emptyLine }}</div>
</a-tooltip>
</template>
</a-table>
</div>
<div class="custom-drawer-style-bottom-btn">
<a-button @click="handleCancel">{{ $t('cancel') }}</a-button>
<a-button @click="handleSubmit" type="primary" :loading="confirmLoading">{{ $t('submit') }}</a-button>
</div>
</a-drawer>
</template>
<script>
import { allocationStandardList, allocationStandard } from '@/api/enterpriseStandardLibraryApi'
export default {
name: 'AllocationStandardDrawer',
props: {
query: {
type: Object,
required: false,
default: () => {
return {}
}
}
},
data () {
return {
visible: false,
confirmLoading: false,
labelCol: {
sm: 8
},
wrapperCol: {
sm: 14
},
selectedRowKeys: [],
selectedRowList: [],
loading: false,
queryParam: {},
/* 分页参数 */
ipagination: {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + ' ' + total + ' ' + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
},
columns: [
{
title: this.$t('standardSelect.standardNumber'),
dataIndex: 'standardNumber',
align: 'center',
width: 150,
scopedSlots: { customRender: 'detail' }
},
{
title: this.$t('standardSelect.standardName'),
dataIndex: 'standardName',
align: 'center',
width: 150,
scopedSlots: { customRender: 'detail' }
},
{
title: this.$t('standardSelect.releaseDate'),
dataIndex: 'releaseDate',
align: 'center',
width: 120,
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('standardSelect.textState'),
dataIndex: 'standardState_dictText',
align: 'center',
width: 120,
scopedSlots: { customRender: 'text' }
}
],
dataList: [],
nodeCode: '' // 选中节点code
}
},
methods: {
open (nodeCode) {
this.visible = true
this.queryParam = {}
this.nodeCode = nodeCode
this.replacePage()
},
// 获取表格数据
replacePage () {
const query = {
...this.queryParam,
pageNo: this.ipagination.current,
pageSize: this.ipagination.pageSize,
nodeCode: this.nodeCode,
source: '3'
}
this.selectedRowKeys = []
this.loading = true
allocationStandardList(query).then((res) => {
if (res.success) {
this.dataList = res.result.records
if (res.result.total) {
this.ipagination.total = res.result.total
} else {
this.ipagination.total = 0
}
} else {
this.dataList = []
}
}).finally(() => {
this.loading = false
})
},
searchQuery () {
this.replacePage()
},
searchReset () {
this.queryParam = {}
this.replacePage()
},
// 表格选择改变
onSelectChange (value, row) {
this.selectedRowKeys = value
this.selectedRowList = row
},
handleTableChange (pagination) {
// 分页、排序、筛选变化时触发
this.ipagination = pagination
this.replacePage()
},
handleCancel () {
this.visible = false
},
handleSubmit () {
if (this.selectedRowKeys && this.selectedRowKeys.length > 0) {
const params = {
nodeCode: this.nodeCode,
ids: this.selectedRowKeys.join(',')
}
allocationStandard(params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
} else {
this.$message.warn(res.message)
}
})
this.visible = false
} else {
this.$message.warning(this.$t('pleaseSelectOneStandard'))
}
},
/**
* 跳转详情页
* @param id
*/
toDetailPage ({ id }) {
this.$openPageNewSheet({
path: '/enterpriseStandardLibrary/enterpriseStandardDetail',
query: {
id
}
})
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,198 @@
<template>
<j-modal
:title="title"
:maskClosable="true"
:width="500"
:closable="true"
@cancel="handleCancel"
fullscreen
@ok="handleOk"
:visible="visible">
<div class="content">
<!-- 左侧文件预览 -->
<div class="content-left">
<a-spin :spinning="leftLoading" class="left-spin">
<iframe v-if="iframeSrc" :src="iframeSrc" width="100%" frameborder="0"></iframe>
<a-empty class="empty-file" v-else :description="$t('noFileAvailableForPreview')"></a-empty>
</a-spin>
</div>
<!-- 右侧表单区域 -->
<div class="content-right">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('enterpriseStandardLibrary.standard.subjectToClause')">
<a-input :placeholder="$t('pleaseEnter') + $t('enterpriseStandardLibrary.standard.subjectToClause')"
:maxLength="50"
v-decorator.trim="[ 'clause', validatorRules.clause ]" />
</a-form-item>
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('enterpriseStandardLibrary.standard.standardContentOrRequirements')">
<a-input :placeholder="$t('pleaseEnter') + $t('enterpriseStandardLibrary.standard.standardContentOrRequirements')"
:maxLength="500"
:rows="4"
type="textarea"
v-decorator.trim="[ 'requirement', validatorRules.requirement ]" />
</a-form-item>
</a-form>
</a-spin>
</div>
</div>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import { getFileInfo } from '@/api/api'
import { previewPdf } from '@/utils/previewPdf'
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
const Base64 = require('js-base64').Base64
export default {
name: 'CheckContentModal',
data () {
return {
title: '',
visible: false,
confirmLoading: false,
leftLoading: false,
form: this.$form.createForm(this),
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
validatorRules: {
clause: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('enterpriseStandardLibrary.standard.subjectToClause') }
],
validateTrigger: 'blur'
},
requirement: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('enterpriseStandardLibrary.standard.standardContentOrRequirements') }
],
validateTrigger: 'blur'
}
},
index: undefined, // 编辑的表格的下标
iframeSrc: '' // 左侧预览的路径
}
},
methods: {
add (fileId) {
this.leftLoading = true
this.getFileInfo(fileId).then(res => {
this.handlePreview(res)
}).finally(() => {
this.leftLoading = false
})
this.visible = true
this.form.resetFields()
},
edit (fileId, record, index) {
this.leftLoading = true
this.getFileInfo(fileId).then(res => {
this.handlePreview(res)
}).finally(() => {
this.leftLoading = false
})
this.visible = true
this.index = index
this.form.resetFields()
this.model = Object.assign({}, record)
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'clause', 'requirement'))
})
},
getFileInfo (fileId) {
return new Promise(resolve => {
let fileInfo
getFileInfo({ id: fileId }).then(res => {
if (res.success) {
fileInfo = res.result
}
}).finally(() => {
resolve(fileInfo)
})
})
},
handlePreview (file) {
const fileSuffix = file.fileName ? file.fileName.split('.')[file.fileName.split('.').length - 1].toLowerCase() : ''
if (fileSuffix === 'pdf') {
// pdf预览
const url = previewPdf(file.id)
this.iframeSrc = url
} else if (fileSuffix === 'docx' || fileSuffix === 'doc') {
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.fileName}`
// word文件仍使用KKFile进行预览
const url = `${window._CONFIG.onlinePreviewDomainURL}?url=${encodeURIComponent(Base64.encode(fileFullUrl))}`
this.iframeSrc = url
}
},
close () {
this.visible = false
this.model = {}
this.index = undefined
this.iframeSrc = ''
},
handleCancel () {
this.close()
},
handleOk () {
if (this.confirmLoading) return
this.form.validateFields((err, values) => {
if (!err) {
this.confirmLoading = true
const param = Object.assign({}, this.model, values)
console.log(param)
this.$emit('ok', param, this.index)
this.close()
this.confirmLoading = false
}
})
}
}
}
</script>
<style scoped lang="less">
.content {
width: 100%;
height: 100%;
display: flex;
.content-left {
width: 60%;
height: 100%;
overflow-y: scroll;
.left-spin {
height: 100%;
}
/deep/ .ant-spin-container {
height: 100%;
}
iframe {
height: calc(100% - 20px) !important;
}
}
.content-right {
width: 40%;
height: 100%;
overflow-y: scroll;
}
}
.empty-file {
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
</style>
@@ -0,0 +1,286 @@
<template>
<a-drawer
:title="title"
:width="width"
placement="right"
:closable="true"
@close="handleCancel"
:visible="visible"
:maskClosable="false"
destroyOnClose
class="custom-drawer-style"
>
<div class="custom-drawer-style-scroll">
<a-spin :spinning="confirmLoading" style="width: 100%;height:100%">
<!-- 引用标准可以选国标和外标 -->
<add-form ref="addForm"
:flag="StandardSource.ENTERPRISE.value"
:defaultValue="{year_number: new Date().getFullYear() + '', try_flag: '0'}"
:disabledFieldList="disabledFieldList"
:selectStandardCanUpdateTypeField="['reference_standard']"
:hiddenFieldList="hiddenFieldList"
:special-placeholder="specialPlaceholder"
:get-form-func="getFormFunc"
:get-document-info-func="getDocumentInfoFunc"
:exclude-standard-fields="['substitute_standard_number', 'reference_standard']"
@submitFormData="submitFormData" />
<div class="header-text-btn">
<!-- 稽查内容 -->
<p class="header-text">{{ $t('enterpriseStandardLibrary.standard.auditContent') }}</p>
<a-button icon="plus" type="primary" @click="handleAdd">{{ $t('newlyAdded') }}</a-button>
</div>
<div class="check-content-table">
<a-table
:scroll="{x: 500}"
:columns="columns"
:dataSource="dataSource"
:pagination="false"
rowKey="id">
<template slot="text" slot-scope="text">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<div class="table-text">{{ text || text === 0 ? text : global.emptyLine }}</div>
</a-tooltip>
</template>
<!-- 操作栏 -->
<template slot="action" slot-scope="text, record, index" class="action-span-cell">
<a @click="handleEdit(record, index)" class="table-ope-btn">{{ $t('edit') }}</a>
<a @click="handleDelete(index)" class="table-ope-btn" style="margin-left: 8px">{{ $t('delete') }}</a>
</template>
</a-table>
</div>
</a-spin>
</div>
<div class="custom-drawer-style-bottom-btn">
<a-button @click="handleOk" type="primary" style="margin-bottom: 0;">
{{ $t('preservation') }}
</a-button>
<a-button @click="handleCancel" style="margin-bottom: 0;">{{ $t('close') }}</a-button>
</div>
<check-content-modal ref="checkContentModal" @ok="checkContentOk"></check-content-modal>
</a-drawer>
</template>
<script>
import '@assets/less/common.less'
import AddForm from '@/components/customField/AddForm'
import { getEnterpriseStandardAddForm, getEnterpriseStandardInfoById } from '@/api/enterpriseStandardLibraryApi'
import { postAction } from '@/api/manage'
import CheckContentModal from './CheckContentModal'
import { StandardSource } from '@/enums/commonEnums'
export default {
name: 'EnterpriseStandardModal',
components: { CheckContentModal, AddForm },
computed: {
// 需要隐藏的字段
hiddenFieldList () {
if (this.type === 'add') {
return ['standard_number']
} else if (this.type === 'edit') {
return ['standard_code', 'name_code', 'category_code', 'standard_number_temp']
} else {
return []
}
},
// 不可更改的字段
disabledFieldList () {
if (this.type === 'edit') {
return ['standard_number', 'replaced_by_standard_number', 'referenced_standard', 'year_number']
} else {
return ['standard_number', 'replaced_by_standard_number', 'referenced_standard']
}
}
},
data () {
return {
StandardSource,
visible: false,
title: '',
width: 800,
// 存储表单的数据
form: {},
confirmLoading: false,
getFormFunc: getEnterpriseStandardAddForm,
getDocumentInfoFunc: getEnterpriseStandardInfoById,
columns: [
{
title: this.$t('enterpriseStandardLibrary.standard.subjectToClause'),
align: 'center',
width: 180,
dataIndex: 'clause',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('enterpriseStandardLibrary.standard.standardContentOrRequirements'),
align: 'center',
width: 180,
dataIndex: 'requirement',
scopedSlots: { customRender: 'text' }
},
{ // 整改部门
title: this.$t('enterpriseStandardLibrary.standard.rectificationDepartment'),
align: 'center',
width: 200,
dataIndex: 'rectificationDepartment',
customRender: (value, row, index) => {
return <j-select-depart v-model={this.dataSource[index].rectificationDepartment}
backDepart={true} />
}
},
{
title: this.$t('operation'),
scopedSlots: { customRender: 'action' },
align: 'center',
width: 150
}
],
dataSource: [],
type: '', // 是新增还是编辑
// 特殊的placeholder
specialPlaceholder: {
standard_number_temp: `${this.$t('enterpriseStandardLibrary.standard.inputExample')}0001${this.$t('enterpriseStandardLibrary.standard.nullAutoCreate')}`
},
url: {
add: '/laws/standard/enterprise/commonAdd',
edit: '/laws/standard/enterprise/commonEdit'
}
}
},
methods: {
add () {
this.visible = true
this.type = 'add'
this.$nextTick(() => {
this.$refs.addForm.add()
})
},
edit (record) {
this.visible = true
this.type = 'edit'
this.$nextTick(() => {
this.$refs.addForm.edit(record, (res) => {
this.dataSource = res.checkList
})
})
},
// 稽查内容的添加
handleAdd () {
const value = this.$refs.addForm.formInline
// 过程稿件id 发布稿>修改单>报批稿>送审稿>征求意见稿>草稿
const fieldArr = ['publish_of_original', 'modification_list', 'draft_for_review', 'draft_for_comment', 'draft']
const needSuffixArr = ['doc', 'docx', 'pdf']
let fileId = ''
for (const item of fieldArr) {
if (!fileId && value[item + '_dictText']) {
const fileNameArr = value[item + '_dictText'].split(',')
for (let i = 0; i < fileNameArr.length; i++) {
// 截取文件后缀名
const fileSuffix = (fileNameArr[i] ? fileNameArr[i].split('.')[fileNameArr[i].split('.').length - 1] : '').toLowerCase()
if (needSuffixArr.includes(fileSuffix)) {
// 说明是可以预览的文件格式
console.log('支持文件预览的文件', value[item].split(',')[i], value[item + '_dictText'].split(',')[i])
fileId = value[item].split(',')[i]
}
}
}
}
this.$refs.checkContentModal.title = this.$t('newlyAdded')
this.$refs.checkContentModal.add(fileId)
},
// 稽查内容的编辑
handleEdit (record, index) {
const value = this.$refs.addForm.formInline
// 过程稿件id 发布稿>修改单>报批稿>送审稿>征求意见稿>草稿
const fieldArr = ['publish_of_original', 'modification_list', 'draft_for_review', 'draft_for_comment', 'draft']
const needSuffixArr = ['doc', 'docx', 'pdf']
let fileId = ''
for (const item of fieldArr) {
if (!fileId && value[item + '_dictText']) {
const fileNameArr = value[item + '_dictText'].split(',')
for (let i = 0; i < fileNameArr.length; i++) {
// 截取文件后缀名
const fileSuffix = (fileNameArr[i] ? fileNameArr[i].split('.')[fileNameArr[i].split('.').length - 1] : '').toLowerCase()
if (needSuffixArr.includes(fileSuffix)) {
// 说明是支持文件预览的文件格式
console.log('支持文件预览的文件', value[item].split(',')[i], value[item + '_dictText'].split(',')[i])
fileId = value[item].split(',')[i]
}
}
}
}
this.$refs.checkContentModal.title = this.$t('edit')
this.$refs.checkContentModal.edit(fileId, record, index)
},
// 稽查内容的删除
handleDelete (index) {
this.dataSource.splice(index, 1)
},
// 稽查内容新增编辑完成
checkContentOk (value, index) {
console.log(value, index)
if (index || index === 0) {
// 有下标,说明是编辑
this.dataSource.splice(index, 1, value)
} else {
// 没有下标, 是新增
this.dataSource.push(value)
}
},
handleOk () {
this.$refs.addForm.submit()
},
// 表单提交的回调,获取表单数据
submitFormData (formInline) {
this.confirmLoading = true
let url = ''
if (formInline.id) {
url = this.url.edit
} else {
url = this.url.add
}
const param = Object.assign({}, formInline)
param.checkList = this.dataSource
postAction(url, param).then((res) => {
if (res.success) {
this.$message.success(this.$t('operationSuccessful'))
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
this.dataSource = []
this.$refs.addForm.clearValidate()
}
}
}
</script>
<style scoped lang="less">
/deep/ .title-text {
width: 130px;
}
.header-text-btn {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
.header-text {
margin-bottom: 0;
}
}
.check-content-table {
padding-bottom: 40px;
}
</style>
@@ -0,0 +1,281 @@
<template>
<j-modal
:title="title"
:maskClosable="true"
:width="1200"
:closable="true"
switchFullscreen
:footer="false"
@cancel="handleCancel"
:visible="visible">
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :sm="8">
<!-- 企标编号 -->
<a-form-item :label="$t('enterpriseStandardLibrary.reviewMeeting.enterpriseStandardNum')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input
:placeholder="$t('pleaseEnter') + $t('enterpriseStandardLibrary.reviewMeeting.enterpriseStandardNum')"
v-model="queryParam.standardNumber">
</a-input>
</a-form-item>
</a-col>
<!-- 授权到期日期 -->
<a-col :sm="8">
<a-form-item :label="$t('enterpriseStandardLibrary.standard.authorizationExpirationDate')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-range-picker v-model="queryParam.authorizationExpirationDate"
format="YYYY-MM-DD" value-format="YYYY-MM-DD"
@change="onChange"></a-range-picker>
</a-form-item>
</a-col>
<a-col :sm="8">
<!-- 授权部门 -->
<a-form-item :label="$t('enterpriseStandardLibrary.standard.authorizationDepart')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-tree-select
tree-node-filter-prop="title"
v-model="queryParam.authorizationDepart"
:maxTagCount="1"
allowClear
:getPopupContainer="triggerNode=> triggerNode.parentNode"
class="box-input"
style="width: 100%"
:tree-data="categoryTreeList"
:placeholder="$t('pleaseSelect') + $t('enterpriseStandardLibrary.standard.authorizationDepart')"
treeCheckable
treeCheckStrictly
show-search
:filterTreeNode="filterTreeOption"
/>
</a-form-item>
</a-col>
<a-col :sm="8">
<!-- 授权人员 -->
<a-form-item :label="$t('enterpriseStandardLibrary.standard.authorizationUser')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input
:placeholder="$t('pleaseEnter') + $t('enterpriseStandardLibrary.standard.authorizationUser')"
v-model="queryParam.authUser">
</a-input>
</a-form-item>
</a-col>
<div style="float: right;overflow: hidden;margin-right: 11px" class="table-page-search-submitButtons">
<a-button class="box-button" type="primary" icon="search" @click="searchQuery">{{ $t('query') }}</a-button>
<a-button class="box-button" icon="reload" style="margin-left: 8px" @click="searchReset">{{ $t('reset') }}</a-button>
</div>
</a-row>
</a-form>
</div>
<j-table
ref="table"
rowKey="id"
:can-drag="true"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:scroll="{x: '100%', y: 'calc(100vh - 500px)'}"
@change="handleTableChange">
<template v-slot:action="{text, record}">
<!-- 解除授权 -->
<a class="table-del" :disabled="isDisabled(record)" @click="handleCancelAuthor(record)">{{$t('enterpriseStandardLibrary.standard.cancelAuthor')}}</a>
</template>
</j-table>
</j-modal>
</template>
<script>
import { JeroListMixin } from '@/mixins/JeroListMixin'
import { cancelAuthor } from '@/api/enterpriseStandardLibraryApi'
import { queryDepartTreeList } from '@/api/api'
import JTable from '@/components/jero/JTable'
import { getAction } from '../../../../api/manage'
export default {
name: 'TemporaryPermissionRecord',
mixins: [JeroListMixin],
components: { JTable },
data () {
return {
title: this.$t('enterpriseStandardLibrary.standard.temporaryPermissionRecord'),
visible: false,
labelCol: {
span: 4
},
wrapperCol: {
span: 14
},
disableMixinCreated: true,
categoryTreeList: [], // 组织机构下拉框数据
columns: [
{
title: this.$t('serialNumber'),
dataIndex: '',
key: 'rowIndex',
align: 'center',
width: 80,
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{ // 授权日期
title: this.$t('enterpriseStandardLibrary.standard.authorizationDate'),
align: 'center',
width: 180,
dataIndex: 'createTime',
scopedSlots: { customRender: 'text' }
},
{ // 授权到期日期
title: this.$t('enterpriseStandardLibrary.standard.maturityDate'),
align: 'center',
width: 180,
dataIndex: 'authExpireDate',
scopedSlots: { customRender: 'text' }
},
{ // 授权部门
title: this.$t('enterpriseStandardLibrary.standard.authorizationDepart'),
align: 'center',
width: 180,
dataIndex: 'authDept_dictText',
scopedSlots: { customRender: 'text' }
},
{ // 授权人员
title: this.$t('enterpriseStandardLibrary.standard.authorizationUser'),
align: 'center',
width: 180,
dataIndex: 'authUser_dictText',
scopedSlots: { customRender: 'text' }
},
{ // 授权企标
title: this.$t('enterpriseStandardLibrary.standard.authorizationStandard'),
align: 'center',
width: 180,
dataIndex: 'standardNumber',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('operation'),
scopedSlots: { customRender: 'action' },
align: 'center',
fixed: 'right',
width: 100
}
],
url: {
list: '/laws/standard/enterprise/tempAuthRecord'
}
}
},
mounted () {
this.getSysCategoryTree()
},
methods: {
open () {
this.visible = true
this.loadData()
},
// 获取组织机构树
getSysCategoryTree () {
queryDepartTreeList().then((res) => {
if (res.success) {
this.categoryTreeList = res.result
} else {
this.categoryTreeList = []
}
})
},
// 解除授权按钮是否不可点击
isDisabled (record) {
const authExpireDate = new Date(Date.parse(record.authExpireDate))
const currentDate = new Date().getFullYear() + '-' + (new Date().getMonth() + 1) + '-' + new Date().getDate()
return new Date(Date.parse(authExpireDate)) < new Date(Date.parse(currentDate))
},
loadData (arg) {
if (!this.url.list) {
this.$message.warning('请设置url.list属性!')
return
}
// 加载数据 若传入参数1则加载第一页的内容
if (arg === 1) {
this.ipagination.current = 1
}
const params = this.getQueryParams()// 查询条件
if (params.authorizationDepart && params.authorizationDepart.length > 0) {
params.authDept = params.authorizationDepart.map(item => item.value).join(',')
}
this.loading = true
getAction(this.url.list, params).then((res) => {
if (res.success) {
// update-begin---author:zhangyafei Date:20201118 for:适配不分页的数据列表------------
this.dataSource = res.result.records || res.result
if (res.result.total) {
this.ipagination.total = res.result.total
} else {
this.ipagination.total = 0
}
// update-end---author:zhangyafei Date:20201118 for:适配不分页的数据列表------------
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.loading = false
})
},
filterTreeOption (input, option) {
const text = option.componentOptions.propsData.title || option.componentOptions.propsData.label
return text.toLowerCase().indexOf(input.toLowerCase()) >= 0
},
onChange () {
if (this.queryParam.authorizationExpirationDate && this.queryParam.authorizationExpirationDate.length > 0) {
this.queryParam.startAuthExpireDate = this.queryParam.authorizationExpirationDate[0]
this.queryParam.endAuthExpireDate = this.queryParam.authorizationExpirationDate[1]
} else {
this.queryParam.authorizationExpirationDate = []
}
},
// 解除授权
handleCancelAuthor (record) {
this.$confirm({
title: this.$t('enterpriseStandardLibrary.standard.cancelAuthor'),
content: this.$t('enterpriseStandardLibrary.standard.confirmCancelAuthor'),
onOk: () => {
const params = {}
params.id = record.id
cancelAuthor(params).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loadData()
} else {
this.$message.warning(res.message)
}
})
}
})
},
handleCancel () {
this.close()
},
close () {
this.queryParam = {}
this.visible = false
this.ipagination = {
current: 1,
pageSize: 10,
pageSizeOptions: ['10', '30', '50', '100', '150', '200'],
showTotal: (total, range) => {
return range[0] + '-' + range[1] + ' ' + this.$t('total') + total + this.$t('strip')
},
showQuickJumper: true,
showSizeChanger: true,
total: 0
}
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,135 @@
<template>
<j-modal
:title="title"
:maskClosable="true"
:width="800"
:closable="true"
switchFullscreen
@cancel="handleCancel"
@ok="handleOk"
:visible="visible">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('enterpriseStandardLibrary.standard.authorizationDepart')">
<j-select-depart
v-decorator.trim="[ 'deptIds', validatorRules.deptIds]"
:multi="true"
:backDepart="true"
:treeOpera="true">
</j-select-depart>
</a-form-item>
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('enterpriseStandardLibrary.standard.authorizationUser')">
<user-selection
type="checkbox"
v-decorator.trim="[ 'userIds', validatorRules.userIds]"
:nameStr="model.userNames"
:placeholder="$t('pleaseSelect') + $t('enterpriseStandardLibrary.standard.authorizationUser')"
@nameChange="userSelectNameChange">
</user-selection>
</a-form-item>
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('enterpriseStandardLibrary.standard.maturityDate')">
<a-date-picker
v-decorator.trim="[ 'authDate', validatorRules.authDate]"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
>
</a-date-picker>
</a-form-item>
</a-form>
</a-spin>
</j-modal>
</template>
<script>
import { temporaryPermissionSet } from '@/api/enterpriseStandardLibraryApi'
import UserSelection from '@/components/selection/UserSelection'
export default {
name: 'TemporaryPermissionSet',
components: { UserSelection },
data () {
return {
title: this.$t('enterpriseStandardLibrary.standard.temporaryPermissionSet'),
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
validatorRules: {
deptIds: {
rules: [
{ required: false, message: this.$t('pleaseEnter') + this.$t('enterpriseStandardLibrary.standard.authorizationDepart') }
],
validateTrigger: 'change'
},
userIds: {
rules: [
{ required: false, message: this.$t('pleaseEnter') + this.$t('enterpriseStandardLibrary.standard.authorizationUser') }
],
validateTrigger: 'change'
},
authDate: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('enterpriseStandardLibrary.standard.maturityDate') }
],
validateTrigger: 'change'
}
}
}
},
methods: {
open (standards) {
this.visible = true
this.form.resetFields()
this.model.standards = standards
},
userSelectNameChange (value) {
this.model.userNames = value
},
handleCancel () {
this.close()
},
handleOk () {
if (this.confirmLoading) return
this.form.validateFields((err, values) => {
if (!err) {
if (!values.deptIds && !values.userIds) {
this.$message.warning(this.$t('enterpriseStandardLibrary.standard.pleaseSelectUserOrDepart'))
return
}
this.confirmLoading = true
const param = Object.assign({}, values)
param.standardIds = this.model.standards
temporaryPermissionSet(param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
close () {
this.visible = false
this.model = {}
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,121 @@
<template>
<j-modal
:title="title"
:maskClosable="true"
:width="500"
:closable="true"
switchFullscreen
@cancel="handleCancel"
@ok="handleOk"
:visible="visible">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" :label="$t('enterpriseStandardLibrary.standard.nodeName')">
<a-input :placeholder="$t('pleaseEnter') + $t('enterpriseStandardLibrary.standard.nodeName')"
:maxLength="50"
v-decorator.trim="[ 'nodeName', validatorRules.nodeName ]" />
</a-form-item>
</a-form>
</a-spin>
</j-modal>
</template>
<script>
import pick from 'lodash.pick'
import { postAction } from '@/api/manage'
export default {
name: 'TreeNodeModal',
data () {
return {
title: '',
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
validatorRules: {
nodeName: {
rules: [
{ required: true, message: this.$t('pleaseEnter') + this.$t('enterpriseStandardLibrary.standard.nodeName') }
],
validateTrigger: 'blur'
}
},
url: {
edit: '/laws/standard/lawsTreeNode/enterprise/edit',
add: '/laws/standard/lawsTreeNode/enterprise/add'
}
}
},
methods: {
add (nodeObj) {
console.log(nodeObj)
this.visible = true
this.form.resetFields()
this.model.key = nodeObj.id
this.model.nodeCode = nodeObj.nodeCode
},
edit (record) {
this.visible = true
this.form.resetFields()
this.model = Object.assign({}, record)
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'nodeName'))
})
},
close () {
this.visible = false
this.model = {}
},
handleCancel () {
this.close()
},
handleOk () {
if (this.confirmLoading) return
this.form.validateFields((err, values) => {
if (!err) {
this.confirmLoading = true
const param = Object.assign({}, values)
if (!this.model.id) {
param.parentId = this.model.key
param.parentCode = this.model.nodeCode
} else {
param.id = this.model.id
}
let url = ''
if (this.model.id) {
url = this.url.edit
} else {
url = this.url.add
}
postAction(url, param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.$emit('ok')
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
}
}
}
</script>
<style scoped>
</style>
+17
View File
@@ -0,0 +1,17 @@
<template>
<exception-page type="403" />
</template>
<script>
import ExceptionPage from './ExceptionPage'
export default {
components: {
ExceptionPage
}
}
</script>
<style scoped>
</style>
+17
View File
@@ -0,0 +1,17 @@
<template>
<exception-page type="404" />
</template>
<script>
import ExceptionPage from './ExceptionPage'
export default {
components: {
ExceptionPage
}
}
</script>
<style scoped>
</style>
+17
View File
@@ -0,0 +1,17 @@
<template>
<exception-page type="500" />
</template>
<script>
import ExceptionPage from './ExceptionPage'
export default {
components: {
ExceptionPage
}
}
</script>
<style scoped>
</style>
+91
View File
@@ -0,0 +1,91 @@
<template>
<div class="exception">
<div class="img">
<img :src="config[type].img" alt="" />
</div>
<div class="content">
<h1>{{ config[type].title }}</h1>
<div class="desc">{{ config[type].desc }}</div>
<div class="action">
<a-button type="primary" @click="handleToHome">返回首页</a-button>
</div>
</div>
</div>
</template>
<script>
import types from './type'
export default {
name: 'Exception',
props: {
type: {
type: String,
default: '404'
}
},
data () {
return {
config: types
}
},
methods: {
handleToHome () {
this.$router.push({ name: 'dashboard' })
}
}
}
</script>
<style lang="less" scoped>
.exception {
height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.img > img {
height: 260px;
}
.content {
margin-top: 24px;
h1 {
font-size: 20px;
font-family: PingFang SC-Medium, PingFang SC, sans-serif;
color: #1D2129;
text-align: center;
font-weight: bold;
}
.desc {
font-size: 14px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
color: #86909C;
line-height: 22px;
}
}
.action {
margin-top: 32px;
text-align: center;
}
}
.mobile {
.exception {
margin-top: 30px;
.img {
padding-right: unset;
img {
height: 40%;
max-width: 80%;
}
}
}
}
</style>
+20
View File
@@ -0,0 +1,20 @@
const noPermissionImage = require('../../assets/404.png')
const types = {
403: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/wZcnGqRDyhPOEYFcZDnb.svg',
title: '403',
desc: '抱歉,你无权访问该页面'
},
404: {
img: noPermissionImage,
title: '暂无权限',
desc: '抱歉,您无权限访问此页面,请联系管理员'
},
500: {
img: 'https://gw.alipayobjects.com/zos/rmsportal/RVRUAYdCGeYNBWoKiIwB.svg',
title: '500',
desc: '抱歉,服务器出错了'
}
}
export default types
@@ -0,0 +1,95 @@
<template>
<div class="box">
<div class="box-header">
<a-icon type="left-circle" @click="iconClick" class="icon" theme="filled"/>
<span class="box-header-left-text">
{{this.$route.query.msgContent}}
</span>
<span class="box-header-right-text">
{{$t('message.my.notificationTime')}}: {{this.$route.query.sendTime || this.$route.query.createTime}}
</span>
</div>
<div class="box-content" v-html="this.$route.query.msgContentInfo">
<!-- {{this.$route.query.msgContentInfo}}-->
</div>
</div>
</template>
<script>
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
export default {
name: 'MessageDetails',
methods: {
iconClick () {
if (this.$route.query.isCustomContentList) {
this.$router.push({
path: '/modules/message/documentDynamics'
})
} else {
this.$router.push({
path: '/isps/userAnnouncement'
})
}
}
},
mounted () {
if (this.$route.query.msgContentInfo && this.$route.query.msgContentInfo.includes('downLoadFile')) {
const index = this.$route.query.msgContentInfo.indexOf('target')
this.$route.query.msgContentInfo = this.$route.query.msgContentInfo.slice(0, index - 2) + '&token=' + Vue.ls.get(ACCESS_TOKEN) + '\' ' +
this.$route.query.msgContentInfo.slice(index)
}
}
}
</script>
<style scoped lang="less">
.box {
width: 100%;
}
.box-header {
padding: 0 32px;
height: 68px;
line-height: 68px;
border-bottom: 2px #eff1f3 solid;
}
.icon {
font-size: 32px;
color: #00A0E9;
margin-right: 30px;
display: inline-block;
float: left;
line-height: 75px;
}
.box-header-left-text {
width: calc(100% - 420px);
font-size: 18px;
display: inline-block;
color: #040B29;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.box-header-right-text {
font-size: 16px;
float: right;
display: inline-block;
line-height: 88px;
color: #040B29;
opacity: 0.5;
}
.box-content {
padding: 40px 70px;
box-sizing: border-box;
color: #040B29;
font-size: 16px;
text-indent: 24px;
/*letter-spacing: 1px*/
}
</style>
@@ -0,0 +1,238 @@
<template>
<a-card :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="消息标题">
<a-input placeholder="请输入消息标题" v-model="queryParam.esTitle"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="发送内容">
<a-input placeholder="请输入发送内容" v-model="queryParam.esContent"></a-input>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="8">
<a-form-item label="接收人">
<a-input placeholder="请输入接收人" v-model="queryParam.esReceiver"></a-input>
</a-form-item>
</a-col>
</template>
<a-col :md="6" :sm="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator">
<a-button @click="handleAdd" v-show="show" type="primary" icon="plus">新增</a-button>
<a-button type="primary" v-show="show" icon="download" @click="handleExportXls('消息')">导出</a-button>
<a-upload v-show="show" name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl"
@change="handleImportExcel">
<a-button type="primary" icon="import">导入</a-button>
</a-upload>
<a-dropdown v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay">
<a-menu-item key="1" @click="batchDel">
<a-icon type="delete"/>
删除
</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> 批量操作
<a-icon type="down"/>
</a-button>
</a-dropdown>
</div>
<!-- table区域-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{
selectedRowKeys.length }}</a>
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<!-- 字符串超长截取省略号显示-->
<span slot="esContent" slot-scope="text">
<j-ellipsis :value="text" :length="10" />
</span>
<span slot="action" slot-scope="text, record">
<a href='javascript:' @click="handleDetail(record)">详情</a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link">更多<a-icon type="down"/></a>
<a-menu slot="overlay">
<a-menu-item v-show="show">
<a @click="handleEdit(record)">编辑</a>
</a-menu-item>
<a-menu-item>
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<a>删除</a>
</a-popconfirm>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<sysMessage-modal ref="modalForm" @ok="modalFormOk"></sysMessage-modal>
</a-card>
</template>
<script>
import SysMessageModal from './modules/SysMessageModal'
import { JeroListMixin } from '@/mixins/JeroListMixin'
import JEllipsis from '@/components/jero/JEllipsis'
export default {
name: 'SysMessageList',
mixins: [JeroListMixin],
components: {
JEllipsis,
SysMessageModal
},
data () {
return {
description: '消息管理页面',
// 新增修改按钮是否显示
show: false,
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{
title: '消息标题',
align: 'center',
dataIndex: 'esTitle'
},
{
title: '发送内容',
align: 'center',
dataIndex: 'esContent',
scopedSlots: { customRender: 'esContent' }
},
{
title: '接收人',
align: 'center',
dataIndex: 'esReceiver'
},
{
title: '发送次数',
align: 'center',
dataIndex: 'esSendNum'
},
{
title: '发送状态',
align: 'center',
dataIndex: 'esSendStatus_dictText'
},
{
title: '发送时间',
align: 'center',
dataIndex: 'esSendTime'
},
{
title: '发送方式',
align: 'center',
dataIndex: 'esType_dictText'
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
scopedSlots: { customRender: 'action' }
}
],
url: {
list: '/sys/message/sysMessage/page',
delete: '/sys/message/sysMessage/delete',
deleteBatch: '/sys/message/sysMessage/deleteBatch',
exportXlsUrl: 'sys/message/sysMessage/exportXls',
importExcelUrl: 'sys/message/sysMessage/importExcel'
}
}
},
computed: {
importExcelUrl: function () {
return `${window._CONFIG.domianURL}/${this.url.importExcelUrl}`
}
},
methods: {}
}
</script>
<style lang="less" scoped>
/** Button按钮间距 */
.ant-btn {
margin-left: 3px
}
.ant-card-body .table-operator {
margin-bottom: 18px;
}
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
.anty-row-operator button {
margin: 0 5px
}
.ant-btn-danger {
background-color: #ffffff
}
.ant-modal-cust-warp {
height: 100%
}
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto
}
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden
}
</style>
@@ -0,0 +1,238 @@
<template>
<a-card :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="模板CODE">
<a-input placeholder="请输入模板CODE" v-model="queryParam.templateCode"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="模板内容">
<a-input placeholder="请输入模板内容" v-model="queryParam.templateContent"></a-input>
</a-form-item>
</a-col>
<template v-if="toggleSearchStatus">
<a-col :md="6" :sm="8">
<a-form-item label="模板标题">
<a-input placeholder="请输入模板标题" v-model="queryParam.templateName"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="模板类型">
<a-input placeholder="请输入模板类型" v-model="queryParam.templateType"></a-input>
</a-form-item>
</a-col>
</template>
<a-col :md="6" :sm="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
<a @click="handleToggleSearch" style="margin-left: 8px">
{{ toggleSearchStatus ? '收起' : '展开' }}
<a-icon :type="toggleSearchStatus ? 'up' : 'down'"/>
</a>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator">
<a-button @click="handleAdd" type="primary" icon="plus">新增</a-button>
<a-button type="primary" icon="upload" @click="handleExportXls('消息模板')">导出</a-button>
<a-upload name="file" :showUploadList="false" :multiple="false" :headers="tokenHeader" :action="importExcelUrl"
@change="handleImportExcel">
<a-button type="primary" icon="download">导入</a-button>
</a-upload>
<a-dropdown v-if="selectedRowKeys.length > 0">
<a-menu slot="overlay">
<a-menu-item key="1" @click="batchDel">
<a-icon type="delete"/>
删除
</a-menu-item>
</a-menu>
<a-button style="margin-left: 8px"> 批量操作
<a-icon type="down"/>
</a-button>
</a-dropdown>
</div>
<!-- table区域-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a style="font-weight: 600">{{
selectedRowKeys.length }}</a>
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<!-- 字符串超长截取省略号显示-->
<span slot="templateContent" slot-scope="text">
<j-ellipsis :value="text" :length="25" />
</span>
<span slot="action" slot-scope="text, record">
<a @click="handleEdit(record)">编辑</a>
<a-divider type="vertical"/>
<a-dropdown>
<a class="ant-dropdown-link">更多 <a-icon type="down"/></a>
<a-menu slot="overlay">
<a-menu-item>
<a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)">
<a>删除</a>
</a-popconfirm>
</a-menu-item>
<a-menu-item>
<a @click="handleTest(record)">发送测试</a>
</a-menu-item>
</a-menu>
</a-dropdown>
</span>
</a-table>
</div>
<!-- table区域-end -->
<!-- 表单区域 -->
<sysMessageTemplate-modal ref="modalForm" @ok="modalFormOk"></sysMessageTemplate-modal>
<sysMessageTest-modal ref="testModal"></sysMessageTest-modal>
</a-card>
</template>
<script>
import SysMessageTemplateModal from './modules/SysMessageTemplateModal'
import SysMessageTestModal from './modules/SysMessageTestModal'
import { JeroListMixin } from '@/mixins/JeroListMixin'
import JEllipsis from '@/components/jero/JEllipsis'
export default {
name: 'SysMessageTemplateList',
mixins: [JeroListMixin],
components: {
JEllipsis,
SysMessageTemplateModal,
SysMessageTestModal
},
data () {
return {
description: '消息模板管理页面',
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{
title: '模板CODE',
align: 'center',
dataIndex: 'templateCode'
},
{
title: '模板标题',
align: 'center',
dataIndex: 'templateName'
},
{
title: '模板内容',
align: 'center',
dataIndex: 'templateContent',
scopedSlots: { customRender: 'templateContent' }
},
{
title: '模板类型',
align: 'center',
dataIndex: 'templateType_dictText'
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
scopedSlots: { customRender: 'action' }
}
],
url: {
list: '/sys/message/sysMessageTemplate/page',
delete: '/sys/message/sysMessageTemplate/delete',
deleteBatch: '/sys/message/sysMessageTemplate/deleteBatch',
exportXlsUrl: 'sys/message/sysMessageTemplate/exportXls',
importExcelUrl: 'sys/message/sysMessageTemplate/importExcel'
}
}
},
computed: {
importExcelUrl: function () {
return `${window._CONFIG.domianURL}/${this.url.importExcelUrl}`
}
},
methods: {
handleTest (record) {
this.$refs.testModal.open(record)
this.$refs.testModal.title = '发送测试'
}
}
}
</script>
<style lang="less" scoped>
/** Button按钮间距 */
.ant-btn {
margin-left: 3px
}
.ant-card-body .table-operator {
margin-bottom: 18px;
}
.ant-table-tbody .ant-table-row td {
padding-top: 15px;
padding-bottom: 15px;
}
.anty-row-operator button {
margin: 0 5px
}
.ant-btn-danger {
background-color: #ffffff
}
.ant-modal-cust-warp {
height: 100%
}
.ant-modal-cust-warp .ant-modal-body {
height: calc(100% - 110px) !important;
overflow-y: auto
}
.ant-modal-cust-warp .ant-modal-content {
height: 90% !important;
overflow-y: hidden
}
</style>
@@ -0,0 +1,181 @@
<template>
<a-drawer
:title="title"
:maskClosable="true"
width=650
placement="right"
:closable="true"
@close="close"
:visible="visible"
style="overflow: auto;padding-bottom: 53px;">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="消息标题">
<a-input placeholder="请输入消息标题" v-decorator="['esTitle', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送内容">
<a-input placeholder="请输入发送内容" v-decorator="['esContent', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送所需参数">
<a-input placeholder="请输入发送所需参数Json格式" v-decorator="['esParam', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="接收人">
<a-input placeholder="请输入接收人" v-decorator="['esReceiver', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送方式">
<j-dict-select-tag :triggerChange="true" dictCode="msgType" v-decorator="[ 'esType', {}]" placeholder="请选择发送方式">
</j-dict-select-tag>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送时间">
<a-date-picker showTime format='YYYY-MM-DD HH:mm:ss' v-decorator="[ 'esSendTime', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送状态">
<j-dict-select-tag :triggerChange="true" dictCode="msgSendStatus" v-decorator="[ 'esSendStatus', {}]" placeholder="请选择发送状态">
</j-dict-select-tag>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送次数">
<a-input-number v-decorator="[ 'esSendNum', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="发送失败原因">
<a-input v-decorator="['esResult', {}]"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="备注">
<a-input v-decorator="['remark', {}]"/>
</a-form-item>
</a-form>
</a-spin>
<div v-show="!disableSubmit">
<a-button style="margin-right: .8rem" @confirm="handleCancel">取消</a-button>
<a-button @click="handleOk" type="primary" :loading="confirmLoading">提交</a-button>
</div>
</a-drawer>
</template>
<script>
import { httpAction } from '@/api/manage'
import pick from 'lodash.pick'
import moment from 'moment'
export default {
name: 'SysMessageModal',
data () {
return {
title: '操作',
visible: false,
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {},
disableSubmit: true,
url: {
add: '/sys/message/sysMessage/add',
edit: '/sys/message/sysMessage/edit'
}
}
},
created () {
},
methods: {
add () {
this.edit({})
},
edit (record) {
this.form.resetFields()
this.model = Object.assign({}, record)
this.visible = true
this.$nextTick(() => {
this.form.setFieldsValue(pick(this.model, 'esContent', 'esParam', 'esReceiver', 'esResult', 'esSendNum', 'esSendStatus', 'esTitle', 'esType', 'remark'))
// 时间格式化
this.form.setFieldsValue({ esSendTime: this.model.esSendTime ? moment(this.model.esSendTime) : null })
})
},
close () {
this.$emit('close')
this.visible = false
},
handleOk () {
const that = this
// 触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
that.confirmLoading = true
let httpurl = ''
const method = 'post'
if (!this.model.id) {
httpurl += this.url.add
// method = 'post'
} else {
httpurl += this.url.edit
// method = 'put'
}
const formData = Object.assign(this.model, values)
// 时间格式化
formData.esSendTime = formData.esSendTime ? formData.esSendTime.format('YYYY-MM-DD HH:mm:ss') : null
console.log(formData)
httpAction(httpurl, formData, method).then((res) => {
if (res.success) {
that.$message.success(res.message)
that.$emit('ok')
} else {
that.$message.warning(res.message)
}
}).finally(() => {
that.confirmLoading = false
that.close()
})
}
})
},
handleCancel () {
this.close()
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,218 @@
<template>
<a-modal
:title="title"
:width="800"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<a-form :form="form">
<a-row :gutter="{ xs: 8, sm: 16, md: 24, lg: 32 }">
<a-col :span="12">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板CODE"
style="margin-right: -35px"
>
<a-input
:disabled="disable"
placeholder="请输入模板编码"
v-decorator="['templateCode', validatorRules.templateCode ]"
/>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板类型">
<j-multi-select-tag :maxTagCount="1" :triggerChange="true" dictCode="msgType" v-decorator="['templateType', validatorRules.templateType ]" placeholder="请选择模板类型">
</j-multi-select-tag>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24" >
<a-col :span="24" pull="2">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板标题"
style="margin-left: -15px">
<a-input
placeholder="请输入模板标题"
v-decorator="['templateName', validatorRules.templateName]"
style="width: 122%"
/>
</a-form-item>
</a-col>
</a-row>
<a-row class="form-row" :gutter="24">
<a-col :span="24" pull="4">
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板内容"
style="margin-left: 4px;width: 126%">
<a-textarea placeholder="请输入模板内容" v-decorator="['templateContent', validatorRules.templateContent ]" :autosize="{ minRows: 8, maxRows: 8 }"/>
</a-form-item>
</a-col>
</a-row>
<!--<a-row class="form-row" :gutter="24">-->
<!-- <a-col :span="24" pull="4">-->
<!-- <a-form-item-->
<!-- v-show="useEditor"-->
<!-- :labelCol="labelCol"-->
<!-- :wrapperCol="wrapperCol"-->
<!-- label="模板内容"-->
<!-- style="margin-left: 4px;width: 126%">-->
<!-- <j-editor v-model="templateEditorContent"></j-editor>-->
<!-- </a-form-item>-->
<!-- </a-col>-->
<!--</a-row>-->
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import { httpAction } from '@/api/manage'
import pick from 'lodash.pick'
import { duplicateCheck } from '@/api/api'
import { getConfusionCode } from '@/utils/util'
export default {
name: 'SysMessageTemplateModal',
data () {
return {
title: '操作',
visible: false,
disable: true,
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
confirmLoading: false,
form: this.$form.createForm(this),
validatorRules: {
templateCode: { rules: [{ required: true, message: '请输入模板CODE!' }, { validator: this.validateTemplateCode }] },
templateName: { rules: [{ required: true, message: '请输入模板标题!' }] },
templateContent: { rules: [] },
templateType: { rules: [{ required: true, message: '请输入模板类型!' }] }
},
url: {
add: '/sys/message/sysMessageTemplate/add',
edit: '/sys/message/sysMessageTemplate/edit'
},
useEditor: false,
templateEditorContent: ''
}
},
created () {
},
methods: {
add () {
this.disable = false
this.edit({})
},
edit (record) {
this.form.resetFields()
this.model = Object.assign({}, record)
// this.useEditor = ((record.templateType + '') === '2' || (record.templateType + '') === '4')
if (this.useEditor) {
this.templateEditorContent = record.templateContent
} else {
this.templateEditorContent = ''
}
this.visible = true
this.$nextTick(() => {
if (this.useEditor) {
this.form.setFieldsValue(pick(this.model, 'templateCode', 'templateName', 'templateTestJson', 'templateType'))
} else {
this.form.setFieldsValue(pick(this.model, 'templateCode', 'templateContent', 'templateName', 'templateTestJson', 'templateType'))
}
})
},
close () {
this.$emit('close')
this.visible = false
this.disable = true
},
handleOk () {
this.model.templateType = this.templateType
const that = this
// 触发表单验证
this.form.validateFields((err, values) => {
if (!err) {
that.confirmLoading = true
let httpurl = ''
const method = 'post'
if (!this.model.id) {
httpurl += this.url.add
// method = 'post'
} else {
httpurl += this.url.edit
// method = 'put'
}
const formData = Object.assign(this.model, values)
// 时间格式化
if (this.useEditor) {
formData.templateContent = this.templateEditorContent
}
console.log(formData)
httpAction(httpurl, formData, method).then((res) => {
if (res.success) {
that.$message.success(res.message)
that.$emit('ok')
} else {
that.$message.warning(res.message)
}
}).finally(() => {
that.confirmLoading = false
that.close()
})
}
})
},
validateTemplateCode (rule, value, callback) {
const confusionCode = getConfusionCode('sys_sms_template', 'template_code')
const params = {
// tableName: 'sys_sms_template',
// fieldName: 'template_code',
confusionCode,
fieldVal: value,
dataId: this.model.id
}
duplicateCheck(params).then((res) => {
if (res.success) {
callback()
} else {
callback(res.message)
}
})
},
handleCancel () {
this.close()
},
handleChangeTemplateType (value) {
// 如果是邮件类型那么则改变模板内容是富文本编辑器
this.useEditor = ((value + '') === '2' || (value + '') === '4')
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,122 @@
<template>
<a-modal
:title="title"
:width="800"
:visible="visible"
:confirmLoading="confirmLoading"
@ok="handleOk"
@cancel="handleCancel"
cancelText="关闭">
<a-spin :spinning="confirmLoading">
<a-form>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板标题">
<a-input disabled v-model="templateName"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="模板内容">
<a-textarea disabled v-model="templateContent" :autosize="{ minRows: 5, maxRows: 8 }"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="测试数据">
<a-textarea placeholder="请输入json格式测试数据" v-model="testData" :autosize="{ minRows: 5, maxRows: 8 }"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="消息类型">
<j-dict-select-tag
v-model="msgType"
placeholder="请选择消息类型"
dictCode="msgType"/>
</a-form-item>
<a-form-item
:labelCol="labelCol"
:wrapperCol="wrapperCol"
label="消息接收方">
<a-input placeholder="请输入消息接收方" v-model="receiver"/>
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<script>
import { httpAction } from '@/api/manage'
export default {
name: 'SysMessageTestModal',
data () {
return {
title: '操作',
visible: false,
model: {},
labelCol: {
xs: { span: 24 },
sm: { span: 5 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
confirmLoading: false,
url: {
send: '/sys/message/sysMessageTemplate/sendMsg'
},
templateName: '',
templateContent: '',
receiver: '',
msgType: '',
testData: '',
sendParams: {}
}
},
methods: {
open (record) {
this.sendParams.templateCode = record.templateCode
this.templateName = record.templateName
this.templateContent = record.templateContent
this.testData = record.templateTestJson
this.visible = true
},
close () {
this.receiver = ''
this.msgType = ''
this.sendParams = {}
this.visible = false
},
handleOk () {
const httpurl = this.url.send
const method = 'post'
this.sendParams.testData = this.testData
this.sendParams.receiver = this.receiver
this.sendParams.msgType = this.msgType
httpAction(httpurl, this.sendParams, method).then((res) => {
if (res.success) {
this.$message.success(res.message)
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
this.close()
})
},
handleCancel () {
this.close()
}
}
}
</script>
<style scoped>
</style>
+182
View File
@@ -0,0 +1,182 @@
<template>
<a-card :bordered="false">
<!-- 查询区域 -->
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<a-form-item label="文件名称">
<a-input placeholder="请输入文件名称" v-model="queryParam.fileName"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<a-form-item label="文件地址">
<a-input placeholder="请输入文件地址" v-model="queryParam.url"></a-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<span style="float: left;overflow: hidden;" class="table-page-search-submitButtons">
<a-button type="primary" @click="searchQuery" icon="search">查询</a-button>
<a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button>
</span>
</a-col>
</a-row>
</a-form>
</div>
<!-- 操作按钮区域 -->
<div class="table-operator">
<!-- <a-button type="primary" icon="download" @click="handleExportXls('文件列表')">导出</a-button>-->
<a-upload
name="file"
:multiple="false"
:action="uploadAction"
:headers="tokenHeader"
:showUploadList="false"
:beforeUpload="beforeUpload"
@change="handleChange">
<a-button>
<a-icon type="upload"/>
文件上传
</a-button>
</a-upload>
</div>
<!-- table区域-begin -->
<div>
<div class="ant-alert ant-alert-info" style="margin-bottom: 16px;">
<i class="anticon anticon-info-circle ant-alert-icon"></i> 已选择 <a
style="font-weight: 600">{{
selectedRowKeys.length }}</a>
<a style="margin-left: 24px" @click="onClearSelected">清空</a>
</div>
<a-table
ref="table"
size="middle"
bordered
rowKey="id"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:rowSelection="{selectedRowKeys: selectedRowKeys, onChange: onSelectChange}"
@change="handleTableChange">
<span slot="action" slot-scope="text, record">
<a @click="handlePreview(record)">预览</a>
<a-divider type="vertical"/>
<a @click="ossDelete(record.id)">删除</a>
</span>
</a-table>
</div>
<!-- table区域-end -->
</a-card>
</template>
<script>
import { JeroListMixin } from '@/mixins/JeroListMixin'
import Vue from 'vue'
import { ACCESS_TOKEN } from '@/store/mutation-types'
const Base64 = require('js-base64').Base64
export default {
name: 'OSSFileList',
mixins: [JeroListMixin],
data () {
return {
description: '文件列表',
// 表头
columns: [
{
title: '#',
dataIndex: '',
key: 'rowIndex',
width: 60,
align: 'center',
customRender: function (t, r, index) {
return parseInt(index) + 1
}
},
{
title: '文件名称',
align: 'center',
dataIndex: 'fileName'
},
{
title: '文件地址',
align: 'center',
dataIndex: 'url'
},
{
title: '操作',
dataIndex: 'action',
align: 'center',
scopedSlots: { customRender: 'action' }
}
],
url: {
upload: '/sys/common/upload',
list: '/sys/oss/file/page',
delete: '/sys/oss/file/delete'
}
}
},
computed: {
uploadAction () {
return window._CONFIG.domianURL + this.url.upload
}
},
methods: {
beforeUpload (file) {
const fileType = file.type
if (fileType === 'image') {
if (fileType.indexOf('image') < 0) {
this.$message.warning('请上传图片')
return false
}
} else if (fileType === 'file') {
if (fileType.indexOf('image') >= 0) {
this.$message.warning('请上传文件')
return false
}
}
return true
},
handleChange (info) {
console.log('ok')
if (info.file.status === 'done') {
if (info.file.response.success) {
this.loadData()
this.$message.success(`${info.file.name} 上传成功!`)
} else {
this.$message.warning(`${info.file.response.message}`)
}
} else if (info.file.status === 'error') {
this.$message.warning(`${info.file.response.message}`)
}
},
ossDelete (id) {
const that = this
that.$confirm({
title: '确认删除',
content: '是否删除选中文件?',
onOk: function () {
that.handleDelete(id)
}
})
},
handlePreview (record) {
if (record && record.url) {
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/download/${record.id}?token=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${record.fileName}`
const url = `${window._CONFIG.onlinePreviewDomainURL}?url=${encodeURIComponent(Base64.encode(fileFullUrl))}`
window.open(url)
}
}
}
}
</script>
<style scoped>
@import '~@assets/less/common.less';
</style>
+62
View File
@@ -0,0 +1,62 @@
<template>
<div class="result">
Monitor
</div>
</template>
<script>
export default {
name: 'Monitor'
}
</script>
<style lang="less" scoped>
.result {
text-align: center;
width: 72%;
margin: 0 auto;
padding: 24px 0 8px;
.icon {
font-size: 72px;
line-height: 72px;
margin-bottom: 24px;
}
.success {
color: #52c41a;
}
.error {
color: red;
}
.title {
font-size: 24px;
color: rgba(0, 0, 0, .85);
font-weight: 500;
line-height: 32px;
margin-bottom: 16px;
}
.description {
font-size: 14px;
line-height: 22px;
color: rgba(0, 0, 0, 0.45);
margin-bottom: 24px;
}
.content {
background: #fafafa;
padding: 24px 40px;
border-radius: 2px;
text-align: left;
}
.action {
margin-top: 32px;
}
}
.mobile {
.result {
width: 100%;
margin: 0 auto;
padding: unset;
}
}
</style>
@@ -0,0 +1,167 @@
<!--我的收藏-->
<template>
<div class="collection">
<a-card :bordered="false">
<div class="table-page-search-wrapper">
<a-form layout="inline" @keyup.enter.native="searchQuery">
<a-row :gutter="24">
<a-col :md="6" :sm="8">
<!--标准号-->
<a-form-item :label="$t('personalCenter.collection.standardNo')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-input :placeholder="$t('pleaseEnter') + $t('personalCenter.collection.standardNo')"
v-model="queryParam.standardNumber"></j-input>
</a-form-item>
</a-col>
<a-col :md="6" :sm="8">
<!--标准名称-->
<a-form-item :label="$t('personalCenter.collection.standardName')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<j-input :placeholder="$t('pleaseEnter') + $t('personalCenter.collection.standardName')"
v-model="queryParam.standardName"></j-input>
</a-form-item>
</a-col>
<div style="float: right;overflow: hidden;margin-right: 11px" class="table-page-search-submitButtons">
<a-button class="box-button" type="primary" icon="search" @click="searchQuery" v-has="'personalCenter:myCollection:search'">{{ $t('query') }}</a-button>
<a-button class="box-button" icon="reload" style="margin-left: 8px" @click="searchReset">{{ $t('reset') }}</a-button>
</div>
</a-row>
</a-form>
</div>
<j-table
ref="table"
rowKey="id"
:can-drag="true"
:columns="columns"
:dataSource="dataSource"
:pagination="ipagination"
:loading="loading"
:scroll="{x: '100%', y: yScrollHeight}"
@change="handleTableChange">
<!--标准号标准名称-->
<template v-slot:toDetail="{text, record}">
<a-tooltip overlay-class-name="tooltip-style">
<template slot="title">{{ text || text === 0 ? text : global.emptyLine }}</template>
<a v-if="text" class="link-a" @click="handleGoDetail(record)">{{ text }}</a>
<div v-else class="table-text">{{ global.emptyLine }}</div>
</a-tooltip>
</template>
<template v-slot:action="{text, record}">
<!--取消收藏-->
<a class="table-del" @click="handleCancelCollect(record)" v-has="'personalCenter:myCollection:cancelCollect'">{{$t('personalCenter.collection.cancelCollection')}}</a>
</template>
</j-table>
</a-card>
</div>
</template>
<script>
import JTable from '@/components/jero/JTable'
import { JeroListMixin } from '@/mixins/JeroListMixin'
import { cancelCollect } from '@/api/personCenter'
export default {
name: 'MyCollection',
components: { JTable },
mixins: [JeroListMixin],
data () {
return {
labelCol: {
span: 4
},
wrapperCol: {
span: 14
},
columns: [
{
title: this.$t('personalCenter.collection.standardNo'),
align: 'center',
width: 180,
dataIndex: 'standardNumber',
scopedSlots: { customRender: 'toDetail' }
},
{
title: this.$t('personalCenter.collection.standardName'),
align: 'center',
width: 180,
dataIndex: 'standardName',
scopedSlots: { customRender: 'toDetail' }
},
{
title: this.$t('personalCenter.collection.releaseDate'),
align: 'center',
width: 180,
dataIndex: 'releaseDate',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('personalCenter.collection.newCarTypeDate'),
align: 'center',
width: 180,
dataIndex: 'newModelImplementationDate',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('personalCenter.collection.productCarDate'),
align: 'center',
width: 180,
dataIndex: 'onTheProductionDate',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('personalCenter.collection.standardState'),
align: 'center',
width: 180,
dataIndex: 'standardState',
scopedSlots: { customRender: 'text' }
},
{
title: this.$t('operation'),
scopedSlots: { customRender: 'action' },
align: 'center',
fixed: 'right',
width: 200
}
],
url: {
list: '/personal/lawsStandardCollection/page'
}
}
},
methods: {
// 点击跳转标准详情
handleGoDetail (record) {
let path = ''
// 需要先判断是哪种标准
if (record.source === '1') {
path = '/standardRegulationLibrary/DomesticStandardDetail'
} else if (record.source === '2') {
path = '/standardRegulationLibrary/OverseasStandardDetail'
} else if (record.source === '3') {
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
}
this.$openPageNewSheet({
path: path,
query: {
id: record.standardId
}
})
},
// 取消收藏
handleCancelCollect (record) {
cancelCollect({ id: record.id }).then(res => {
if (res.success) {
this.$message.success(res.message)
this.loadData()
} else {
this.$message.warning(res.message)
}
})
}
}
}
</script>
<style scoped lang="less">
@import '~@assets/less/common.less';
</style>
+45
View File
@@ -0,0 +1,45 @@
<template>
<a-card :bordered="false">
<result :is-success="false" :title="title" :description="description">
<template slot="action">
<a-button type="primary" >返回修改</a-button>
</template>
<div>
<div style="font-size: 16px; color: rgba(0, 0, 0, 0.85); font-weight: 500; margin-bottom: 16px">
您提交的内容有如下错误
</div>
<div style="margin-bottom: 16px">
<a-icon type="close-circle-o" style="color: #f5222d; margin-right: 8px"/>
您的账户已被冻结
<a style="margin-left: 16px">立即解冻 <a-icon type="right" /></a>
</div>
<div>
<a-icon type="close-circle-o" style="color: #f5222d; margin-right: 8px"/>
您的账户还不具备申请资格
<a style="margin-left: 16px">立即升级 <a-icon type="right" /></a>
</div>
</div>
</result>
</a-card>
</template>
<script>
import Result from './Result'
export default {
name: 'Error',
components: {
Result
},
data () {
return {
title: '提交失败',
description: '请核对并修改以下信息后,再重新提交。'
}
}
}
</script>
<style scoped>
</style>
+91
View File
@@ -0,0 +1,91 @@
<template>
<div class="result">
<div>
<a-icon :class="[isSuccess ? 'success' : 'error' ,'icon']" :type="isSuccess ? 'check-circle' : 'close-circle'"/>
</div>
<div class="title" v-if="title">{{ title }}</div>
<div class="description" v-if="description">{{ description }}</div>
<div class="content" v-if="content">
<slot></slot>
</div>
<div class="action">
<slot name="action"></slot>
</div>
</div>
</template>
<script>
export default {
name: 'Result',
// 'isSuccess', 'title', 'description'
props: {
isSuccess: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
description: {
type: String,
default: ''
},
content: {
type: Boolean,
default: true
}
}
}
</script>
<style lang="less" scoped>
.result {
text-align: center;
width: 72%;
margin: 0 auto;
padding: 24px 0 8px;
.icon {
font-size: 72px;
line-height: 72px;
margin-bottom: 24px;
}
.success {
color: #52c41a;
}
.error {
color: red;
}
.title {
font-size: 24px;
color: rgba(0, 0, 0, .85);
font-weight: 500;
line-height: 32px;
margin-bottom: 16px;
}
.description {
font-size: 14px;
line-height: 22px;
color: rgba(0, 0, 0, 0.45);
margin-bottom: 24px;
}
.content {
background: #fafafa;
padding: 24px 40px;
border-radius: 2px;
text-align: left;
}
.action {
margin-top: 32px;
}
}
.mobile {
.result {
width: 100%;
margin: 0 auto;
padding: unset;
}
}
</style>
+92
View File
@@ -0,0 +1,92 @@
<template>
<a-card :bordered="false">
<result :is-success="true" :description="description" :title="title">
<template slot="action">
<a-button type="primary">返回列表</a-button>
<a-button style="margin-left: 8px">查看项目</a-button>
<a-button style="margin-left: 8px">打印</a-button>
</template>
<div>
<div style="font-size: 16px; color: rgba(0, 0, 0, 0.85); font-weight: 500; margin-bottom: 20px;">项目名称</div>
<a-row style="margin-bottom: 16px">
<a-col :xs="24" :sm="12" :md="12" :lg="12" :xl="6">
<span style="color: rgba(0, 0, 0, 0.85)">项目 ID</span>
20180724089
</a-col>
<a-col :xs="24" :sm="12" :md="12" :lg="12" :xl="6">
<span style="color: rgba(0, 0, 0, 0.85)">负责人</span>
曲丽丽是谁
</a-col>
<a-col :xs="24" :sm="24" :md="24" :lg="24" :xl="12">
<span style="color: rgba(0, 0, 0, 0.85)">生效时间</span>
2016-12-12 ~ 2017-12-12
</a-col>
</a-row>
<a-steps :current="1" :direction="isMobile() && directionType.vertical || directionType.horizontal" progressDot>
<a-step >
<span style="font-size: 14px" slot="title">创建项目</span>
<template slot="description">
<div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); position: relative; left: 42px;" slot="description" >
<div style="margin: 8px 0 4px">
曲丽丽
<a-icon style="margin-left: 8px" type="dingding-o" />
</div>
<div>2016-12-12 12:32</div>
</div>
</template>
</a-step>
<a-step title="部门初审">
<span style="font-size: 14px" slot="title">部门初审</span>
<template slot="description">
<div style="font-size: 12px; color: rgba(0, 0, 0, 0.45); position: relative; left: 42px;" slot="description" >
<div style="margin: 8px 0 4px">
周毛毛
<a-icon style="margin-left: 8px; color: #00A0E9" type="dingding-o" />
</div>
<div><a href="">催一下</a></div>
</div>
</template>
</a-step>
<a-step title="财务复核">
<span style="font-size: 14px" slot="title">财务复核</span>
</a-step>
<a-step title="完成" >
<span style="font-size: 14px" slot="title">完成</span>
</a-step>
</a-steps>
</div>
</result>
</a-card>
</template>
<script>
import Result from './Result'
import { mixinDevice } from '@/utils/mixin.js'
const directionType = {
horizontal: 'horizontal',
vertical: 'vertical'
}
export default {
name: 'Success',
components: {
Result
},
mixins: [mixinDevice],
data () {
return {
title: '提交成功',
description: '提交结果页用于反馈一系列操作任务的处理结果,\n' +
' 如果仅是简单操作,使用 Message 全局提示反馈即可。\n' +
' 本文字区域可以展示简单的补充说明,如果有类似展示\n' +
' “单据”的需求,下面这个灰色区域可以呈现比较复杂的内容。',
directionType
}
}
}
</script>
<style scoped>
</style>
@@ -0,0 +1,523 @@
<template>
<div class="detail-page">
<a-spin :spinning="loading">
<div class="detail-page-title">{{ pageTitle }}{{ $t('details') }}</div>
<div class="detail-page-part" v-for="(part, index) in partList" :key="part + index">
<div class="detail-page-part-title">
<span>{{ part }}</span>
<div class="detail-page-part-title-operate-box" v-if="index === 0">
<!-- 更新记录-->
<a-button type="link" @click="handleOpenUpdateRecord" v-has="btnPermission.updateRecord">
<i class="iconfont icon-root-list" />
{{ $t('standardRegulationLibrary.updateRecord') }}
</a-button>
<!-- 提交问题-->
<a-button type="link" @click="submitQuestion" v-has="btnPermission.submitQuestion">
<a-icon type="database" />
{{ $t('standardRegulationLibrary.submitQuestion') }}
</a-button>
<!-- 分享-->
<a-button type="link" @click="handleShare" v-has="btnPermission.share">
<i class="iconfont icon-share-internal" />{{ $t('share') }}
</a-button>
</div>
</div>
<div class="detail-page-part-form" v-if="part !== '过程稿件' && part !== 'Process Manuscript'">
<div class="detail-page-part-form-item" v-for="formItem in form[part]" :key="formItem.id">
<label>{{ formItem.dbFieldTxt }}</label>
<!--标准需要跳转是标准字段且有内容的情况下在这处理-->
<template v-if="standardFields.includes(formItem.dbFieldName) && !!detailData[formItem.dbFieldName]">
<div class="div-form-content">
<template v-for="(standard, standardIndex) in detailData[formItem.dbFieldName].split(',')">
<div class="can-click" :key="'standardKey' + standardIndex" @click="toStandardDetailPage(standard)">{{ standard }}</div>
<div v-if="standardIndex!==detailData[formItem.dbFieldName].split(',').length - 1">,</div>
</template>
</div>
</template>
<!--文件需要显示预览-->
<div class="file-box" v-else-if="fileField.includes(formItem.dbFieldName)">
<template v-if="detailData[formItem.dbFieldName + 'List'] && detailData[formItem.dbFieldName + 'List'].length > 0">
<div class="detail-page-process-manuscript-file"
v-for="file in detailData[formItem.dbFieldName + 'List']"
:key="file.id">
<div class="detail-page-process-manuscript-file-info">
<a-icon type="link" />
<div class="detail-page-process-manuscript-file-name" @click="handlePreview(file)">{{ file.fileName }}</div>
</div>
<!-- 下载-->
<a-button type="link"
icon="download"
v-has="btnPermission.download"
class="file-btn"
@click="handleDownload(file)">
{{
$t('download')
}}
</a-button>
<!-- 无水印下载-->
<!--<a-button type="link" v-has="btnPermission.unwatermarkedDownload" class="file-btn">-->
<!-- <i class="iconfont icon-water-drop-slash"></i>-->
<!-- {{ $t('unwatermarkedDownload') }}-->
<!--</a-button>-->
</div>
</template>
<span v-else>{{ global.emptyLine }}</span>
</div>
<!-- 需要显示查看全部按钮 -->
<a-button v-else-if="showViewAllField.includes(formItem.dbFieldName) &&
(detailData[formItem.dbFieldName + '_dictText'] || detailData[formItem.dbFieldName])"
type="primary" ghost @click="viewAll(formItem.dbFieldName)">
{{ $t('enterpriseStandardLibrary.standard.viewAll')}}
</a-button>
<span v-else>
{{
detailData[needDictFieldList.includes(formItem.fieldShowType) ? formItem.dbFieldName + '_dictText' : formItem.dbFieldName] || global.emptyLine
}}
</span>
</div>
</div>
<!-- 过程稿件特殊处理-->
<div class="detail-page-process-manuscript" v-if="part === '过程稿件' || part === 'Process Manuscript'">
<div class="detail-page-process-manuscript-item" v-for="formItem in form[part]" :key="formItem.id">
<div class="detail-page-process-manuscript-title">
<span>{{ formItem.dbFieldTxt }}</span>
<!-- 标准分解单-->
<a-button type="primary"
ghost
icon="file-text"
@click="previewStandardResolutionSheet(detailData[formItem.dbFieldName + '_standardSplit'])"
v-has="btnPermission.standardResolutionSheet"
:disabled="!detailData[formItem.dbFieldName + '_standardSplit']">
{{ $t('standardRegulationLibrary.standardResolutionSheet') }}
</a-button>
</div>
<div class="detail-page-process-manuscript-file"
v-for="file in detailData[formItem.dbFieldName + 'List']"
:key="file.id">
<div class="detail-page-process-manuscript-file-info">
<a-icon type="link" />
<div class="detail-page-process-manuscript-file-name" @click="handlePreview(file)">{{ file.fileName }}</div>
</div>
<div class="detail-page-process-manuscript-file-operate">
<!-- 下载-->
<a-button type="link" icon="download" v-has="btnPermission.download" @click="handleWatermarkedDownload(file)">
{{ $t('download') }}
</a-button>
<!-- 无水印下载-->
<a-button type="link" v-has="btnPermission.unwatermarkedDownload" @click="handleDownload(file)">
<i class="iconfont icon-water-drop-slash"></i>
{{ $t('unwatermarkedDownload') }}
</a-button>
</div>
</div>
</div>
</div>
</div>
</a-spin>
<div id="images">
<div class="image" v-viewer="{movable: false}">
<img v-show="image" :src="imageUrl" alt="">
</div>
</div>
<!-- 更新记录-->
<update-record-modal ref="uploadRecordModal" />
<!-- 提交问题-->
<submit-question-modal ref="submitQuestionModal" />
<!-- 分享选人-->
<user-select-modal ref="userSelectModal" type="checkbox" @change="continueShare" />
<!-- 标准分解单-->
<standard-resolution-sheet-modal ref="standardResolutionSheetModal" />
<!-- 查看全部弹框 -->
<text-content-modal ref="textContentModal"></text-content-modal>
</div>
</template>
<script>
import '@assets/less/common.less'
import { FieldType, StandardSource } from '../../../enums/commonEnums'
import {
getDomesticStandardFormModal,
shareDomesticStandard,
getDomesticStandardDetail,
getOverseasStandardForm,
getOverseasStandardDocumentInfo,
shareOverseasStandard
} from '../../../api/standardRegulationLibraryApi'
import UpdateRecordModal from './modules/UpdateRecordModal.vue'
import SubmitQuestionModal from './modules/SubmitQuestionModal.vue'
import UserSelectModal from '../../../components/selection/UserSelectModal.vue'
import StandardResolutionSheetModal from './modules/StandardResolutionSheetModal.vue'
import { getFileInfo } from '../../../api/api.js'
import Vue from 'vue'
import { ACCESS_TOKEN } from '../../../store/mutation-types.js'
import { downloadFile, getFileAccessHttpUrl } from '../../../api/manage.js'
import { previewPdf } from '../../../utils/previewPdf.js'
import { Base64 } from 'js-base64'
import { kkFilePreview } from '../../../utils/kkFilePreview'
import { previewFileCurrentWindowByFileId, queryStandardInfoByStandardNumber } from '../../../utils/util'
import TextContentModal from '../../enterpriseStandardLibrary/standard/detail/module/TextContentModal'
// 国内标准权限
const DomesticStandardPermission = {
updateRecord: 'domesticStandard:updateRecord', // 更新记录
submitQuestion: 'domesticStandard:submitQuestion', // 提交问题
share: 'domesticStandard:share', // 分享
standardResolutionSheet: 'domesticStandard:standardResolutionSheet', // 标准分解单
download: 'domesticStandard:download', // 下载
unwatermarkedDownload: 'domesticStandard:unwatermarkedDownload' // 无水印下载
}
// 海外标准权限
const OverseasStandardPermission = {
updateRecord: 'overseasStandard:updateRecord', // 更新记录
submitQuestion: 'overseasStandard:submitQuestion', // 提交问题
share: 'overseasStandard:share', // 分享
standardResolutionSheet: 'overseasStandard:standardResolutionSheet', // 标准分解单
download: 'overseasStandard:download', // 下载
unwatermarkedDownload: 'overseasStandard:unwatermarkedDownload' // 无水印下载
}
// 国内标准接口
const DomesticApi = {
getFormFunc: getDomesticStandardFormModal, // 获取字段
getDetailData: getDomesticStandardDetail, // 获取详情数据
share: shareDomesticStandard // 分享
}
// 海外标准接口
const OverseasApi = {
getFormFunc: getOverseasStandardForm, // 获取字段
getDetailData: getOverseasStandardDocumentInfo, // 获取详情数据
share: shareOverseasStandard // 分享
}
// 支持预览的文件后缀
const CAN_PREVIEW_FILE_SUFFIX = ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx']
const FILE_TYPE_PDF = 'pdf'
const FILE_TYPE_IMGS = ['jpg', 'jpeg', 'png', 'raw']
export default {
name: 'StandardDetailPage',
components: { UpdateRecordModal, SubmitQuestionModal, UserSelectModal, StandardResolutionSheetModal, TextContentModal },
data () {
return {
loading: false,
partList: [], // 页面模块列表
form: {}, // 页面字段
// 页面数据
detailData: {},
// 文件的字段
fileField: [],
// 需要数据字段翻译的属性类型
needDictFieldList: [FieldType.USER_SINGLE.value, FieldType.ORGAN_SINGLE.value, FieldType.ORGAN_MORE.value, FieldType.OPTION_SINGLE.value, FieldType.OPTION_MORE.value, FieldType.TREE.value, FieldType.TREE_SINGLE.value],
image: false,
imageUrl: null,
// 标准字段,需要跳转标准详情
standardFields: ['substitute_standard_number', 'replaced_by_standard_number', 'reference_standard', 'referenced_standard', 'adopt_the_international_standard_number', 'associated_foreign_standards'],
showViewAllField: ['auth_dept'] // 需要显示展示查看全部按钮的字段
}
},
computed: {
type () {
if (this.$route.path.indexOf('Domestic') !== -1) {
return StandardSource.DOMESTIC.value
}
if (this.$route.path.indexOf('Overseas') !== -1) {
return StandardSource.OVERSEAS.value
}
return ''
},
pageTitle () {
if (this.type === StandardSource.DOMESTIC.value) {
return this.$t('system.tag.domesticLaws')
}
if (this.type === StandardSource.OVERSEAS.value) {
return this.$t('system.tag.foreignStandards')
}
return ''
},
// 按钮权限
btnPermission () {
let permission = {}
switch (this.type) {
// 国内标准法规
case StandardSource.DOMESTIC.value:
permission = DomesticStandardPermission
break
// 海外
case StandardSource.OVERSEAS.value:
permission = OverseasStandardPermission
break
default:
break
}
return permission
},
// 操作接口
operateApiFunc () {
let func = {}
switch (this.type) {
case StandardSource.DOMESTIC.value:
func = DomesticApi
break
case StandardSource.OVERSEAS.value:
func = OverseasApi
break
default:
break
}
return func
}
},
async created () {
// 如果是要直接看文件
// if (this.$route.query.viewFile) {
// // TODO 这里需要调一个接口,通过标准id按规则获取文件
// const fileId = '1734843741558288385'
// previewFileCurrentWindowByFileId(fileId).then(() => {
// }).catch((message) => {
// this.$message.warning(message)
// })
// return
// }
await this.initForm()
this.initDetailData()
},
methods: {
/**
* 获取页面字段
*/
initForm () {
return new Promise(resolve => {
this.operateApiFunc.getFormFunc({ type: 1 }).then(res => {
if (res.success) {
const data = res.result || {}
this.partList = Object.keys(data)
const formList = Object.values(data).reduce((acc, cur) => acc.concat(cur), [])
this.fileField = []
formList.forEach(item => {
if (item.fieldShowType === FieldType.FILE_UP.value) {
this.fileField.push(item.dbFieldName)
}
})
this.form = data
} else {
this.$message.warn(res.message)
}
}).finally(() => {
resolve()
})
})
},
// 获取数据
initDetailData () {
this.loading = true
this.operateApiFunc.getDetailData({ id: this.$route.query.id, type: 1 }).then(res => {
if (res.success) {
this.detailData = Object.assign({}, res.result || {})
this.fileField.forEach(item => {
if (this.detailData[item]) {
const fileList = this.detailData[item].split(',')
const fileInfoList = []
fileList.forEach(async id => {
const fileInfo = await this.getFileInfo(id)
if (fileInfo) {
fileInfoList.push(fileInfo)
}
})
this.$set(this.detailData, item + 'List', fileInfoList)
}
})
console.log(this.detailData)
} else {
this.$message.warn(res.message)
}
}).finally(() => {
this.loading = false
})
},
getFileInfo (fileId) {
return new Promise(resolve => {
let fileInfo
getFileInfo({ id: fileId }).then(res => {
if (res.success) {
fileInfo = res.result
}
}).finally(() => {
resolve(fileInfo)
})
})
},
/**
* 更新记录
*/
handleOpenUpdateRecord () {
this.$refs.uploadRecordModal.open(this.detailData.standard_number)
},
/**
* 提交问题
*/
submitQuestion () {
this.$refs.submitQuestionModal.open(this.detailData.standard_number, this.type)
},
/**
* 分享
*/
handleShare () {
this.$refs.userSelectModal.open()
},
/**
* 选择分享的人之后继续分享
* @param ids
*/
continueShare (ids) {
const params = {
standardId: this.$route.query.id, // 要分享的标准的id
standardNumber: this.detailData.standard_number, // 分享的标准编号
recipientId: ids // 分享给的用户的id
}
this.loading = true
this.operateApiFunc.share(params).then(res => {
if (res.success) {
this.$message.success(res.message)
} else {
this.$message.warn(res.message)
}
}).finally(() => {
this.loading = false
})
},
/**
* 查看标准分解单
*/
previewStandardResolutionSheet (id) {
this.$refs.standardResolutionSheetModal.open(id)
},
// 查看全部
viewAll (fieldName) {
const data = this.detailData[fieldName + '_dictText'] || this.detailData[fieldName]
this.$refs.textContentModal.open(data)
},
handlePreview (file) {
if (!file || !file.url) {
return
}
// 截取文件后缀名
const fileSuffix = file.fileName ? file.fileName.split('.')[file.fileName.split('.').length - 1] : ''
const canPreview = CAN_PREVIEW_FILE_SUFFIX.some(tt => fileSuffix.toLowerCase() === tt)
// 判断是否为可预览格式的文件
if (!canPreview) {
this.$message.loading('该文件类型不支持预览,正在为您准备下载...').then(() => {
this.handleDownload(file)
})
return
}
const fileFullUrl = `${window._CONFIG.domianWebSocketURL}/sys/common/view/${file.id}?at=${Vue.ls.get(ACCESS_TOKEN)}&fullfilename=${file.fileName}`
console.log(fileFullUrl)
// 图片预览,使用自己添加的组件
if (canPreview && FILE_TYPE_IMGS.includes(fileSuffix.toLowerCase())) {
this.imageUrl = getFileAccessHttpUrl(file.id)
// 获取viewer实例
const viewer = this.$el.querySelector('.image').$viewer
// 调用show方法进行显示预览图
viewer.show()
// this.$refs.imagePreviewModal.open(file)
return
}
// pdf预览
if (canPreview && FILE_TYPE_PDF.includes(fileSuffix.toLowerCase())) {
const url = previewPdf(file.id)
window.open(url)
return
}
// 其余可预览文件仍使用KKFile进行预览
kkFilePreview(fileFullUrl)
},
/**
* 无水印下载
* @param file
*/
handleDownload (file) {
// 下载文件
downloadFile(`/sys/common/noEncryptDownloadWithoutWaterMark/${file.id}`, file.fileName)
},
/**
* 有水印下载
* @param file
*/
handleWatermarkedDownload (file) {
downloadFile(`/sys/common/noEncryptDownload/${file.id}`, file.fileName)
},
async toStandardDetailPage (standardNumber) {
const standardInfo = await queryStandardInfoByStandardNumber(standardNumber)
if (!standardInfo) {
return
}
let path
// 根据来源跳转不同的详情页
switch (standardInfo.source) {
// 国内
case StandardSource.DOMESTIC.value:
path = '/standardRegulationLibrary/DomesticStandardDetail'
break
// 海外
case StandardSource.OVERSEAS.value:
path = '/standardRegulationLibrary/OverseasStandardDetail'
break
// 企标
case StandardSource.ENTERPRISE.value:
path = '/enterpriseStandardLibrary/enterpriseStandardDetail'
break
default:
break
}
if (!path) {
return
}
const query = {
id: standardInfo.id
}
this.$openPageNewSheet({
path, query
})
}
}
}
</script>
<style scoped lang="less">
.file-box {
flex: 1;
width: 0;
}
.file-btn > ::v-deep span {
display: inline;
color: @primary-color;
font-size: 14px;
}
.div-form-content {
word-break: break-all;
white-space: pre-wrap;
font-size: 16px;
font-family: PingFang SC-Regular, PingFang SC, sans-serif;
color: #1D2129;
-ms-flex: 1;
flex: 1;
width: 0;
}
.div-form-content > div {
display: inline-block;
}
.can-click {
display: inline-block;
color: @primary-color;
cursor: pointer;
}
</style>
@@ -0,0 +1,84 @@
<template>
<j-modal
:title="$t('standardRegulationLibrary.standardResolutionSheet')"
:maskClosable="false"
:width="width"
:visible="visible"
:confirm-loading="confirmLoading"
fullscreen
switchFullscreen
@cancel="handleCancel">
<a-spin :spinning="confirmLoading">
<div class="page-container">
<standard-resolution-sheet ref="resolutionSheet" />
</div>
</a-spin>
<template v-slot:footer>
<a-button @click="handleCancel">{{ $t('close') }}</a-button>
</template>
</j-modal>
</template>
<script>
import StandardResolutionSheet from '../../../../components/StandardResolutionSheet.vue'
export default {
name: 'StandardResolutionSheetModal',
components: { StandardResolutionSheet },
data () {
return {
width: '90%',
visible: false,
confirmLoading: false,
splitId: null,
isFullScreen: false
}
},
methods: {
open (id) {
this.visible = true
this.$nextTick(() => {
const ids = id.split(',')
this.$refs.resolutionSheet.queryParams = {
id: ids[0],
publishBeforeId: ids[1]
}
this.$refs.resolutionSheet.initClauseFieldList()
this.$refs.resolutionSheet.initDetailInfo()
})
},
handleCancel () {
this.visible = false
}
}
}
</script>
<style scoped lang="less">
.page-container {
min-height: 250px;
height: calc(100vh - 200px - 55px - 54px - 48px);
}
/deep/ .ant-modal-body {
background: #F0F2F4;
}
// 处理全屏状态下的样式问题
.j-modal-box.fullscreen .ant-modal-content .ant-modal-body {
.ant-spin-nested-loading {
height: 100%;
/deep/ .ant-spin-container {
height: 100%;
}
}
.page-container {
height: 100%;
}
}
</style>
@@ -0,0 +1,202 @@
<template>
<j-modal
:title="$t('standardRegulationLibrary.submitQuestion')"
:maskClosable="false"
:width="width"
:visible="visible"
:confirm-loading="confirmLoading"
switchFullscreen
@ok="handleOk"
@cancel="handleCancel">
<a-form :form="form">
<a-row :gutter="24">
<a-col :span="12">
<!-- 标题-->
<a-form-item :label="$t('title')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter') + $t('title')"
:maxLength="50"
v-decorator="['title', validatorRules.title]" />
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 项目-->
<a-form-item :label="$t('project')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-select :placeholder="$t('pleaseSelect') + $t('project')"
v-decorator="['project', validatorRules.project]"
show-search
:filter-option="filterOption">
<a-select-option v-for="item in projectSelectOptions" :key="item.id" :title="item.projectName">{{ item.projectName }}</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 标准编号-->
<a-form-item :label="$t('standardRegulationLibrary.standardDetail.standardNumber')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-input :placeholder="$t('pleaseEnter') + $t('standardRegulationLibrary.standardDetail.standardNumber')"
:maxLength="50"
disabled
v-decorator="['standardNumber', validatorRules.standardNumber]" />
</a-form-item>
</a-col>
<a-col :span="12">
<!-- 提问时间-->
<a-form-item :label="$t('standardRegulationLibrary.standardDetail.problemOccurrenceTime')" :labelCol="labelCol" :wrapperCol="wrapperCol">
<a-date-picker :placeholder="$t('pleaseSelect') + $t('standardRegulationLibrary.standardDetail.problemOccurrenceTime')"
:getCalendarContainer="(trigger) => trigger.parentNode"
v-decorator="['quizTime', validatorRules.quizTime]"
format="YYYY-MM-DD HH:mm:ss"
value-format="YYYY-MM-DD HH:mm:ss"
showTime
style="width: 100%" />
</a-form-item>
</a-col>
<a-col :span="24">
<!-- 问题描述-->
<a-form-item :label="$t('standardRegulationLibrary.standardDetail.problemDescription')"
:labelCol="longLabelCol"
:wrapperCol="longWrapperCol">
<a-input :placeholder="$t('pleaseEnter') + $t('standardRegulationLibrary.standardDetail.problemDescription')"
type="textarea"
:maxLength="500"
v-decorator="['problemDescription', validatorRules.problemDescription]" />
<j-upload v-decorator="['file', validatorRules.file]" return-id multiple />
</a-form-item>
</a-col>
</a-row>
</a-form>
</j-modal>
</template>
<script>
import { submitDomesticQuestion, submitOverseasQuestion } from '../../../../api/standardRegulationLibraryApi.js'
import { getCarTypeProjectList } from '../../../../api/projectLibraryApi.js'
import { StandardSource } from '../../../../enums/commonEnums.js'
import moment from 'moment'
export default {
name: 'SubmitQuestionModal',
data () {
return {
width: 800,
visible: false,
confirmLoading: false,
form: this.$form.createForm(this),
modal: {},
labelCol: {
xs: { span: 24 },
sm: { span: 8 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
},
longLabelCol: {
xs: { span: 24 },
sm: { span: 4 }
},
longWrapperCol: {
xs: { span: 24 },
sm: { span: 20 }
},
validatorRules: {
// 标题
title: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('title') }],
validateTrigger: 'blur'
},
// 项目
project: {
rules: [{ required: false, message: this.$t('pleaseSelect') + this.$t('project') }],
validateTrigger: 'change'
},
// 标准编号
standardNumber: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('standardRegulationLibrary.standardDetail.standardNumber') }],
validateTrigger: 'blur'
},
// 问题发生时间
quizTime: {
rules: [{ required: false, message: this.$t('pleaseSelect') + this.$t('standardRegulationLibrary.standardDetail.problemOccurrenceTime') }],
validateTrigger: 'change'
},
// 问题描述
problemDescription: {
rules: [{ required: true, message: this.$t('pleaseEnter') + this.$t('standardRegulationLibrary.standardDetail.problemDescription') }],
validateTrigger: 'blur'
},
// 附件
file: {},
type: null
},
projectSelectOptions: [] // 项目下拉数据
}
},
methods: {
open (standardNumber, type) {
this.visible = true
this.initProjectOptions()
this.type = type
const param = {}
param.standardNumber = standardNumber
param.quizTime = moment().format('YYYY-MM-DD HH:mm:ss')
this.$nextTick(() => {
this.form.setFieldsValue(param)
})
},
// 获取项目下拉框数据
initProjectOptions () {
getCarTypeProjectList({ column: 'createTime', order: 'desc' }).then(res => {
if (res.success) {
this.projectSelectOptions = res.result
} else {
this.$message.warning(res.message)
}
})
},
handleOk () {
if (this.confirmLoading) return
this.form.validateFields((err, values) => {
if (!err) {
let submitFunc
switch (this.type) {
case StandardSource.DOMESTIC.value:
submitFunc = submitDomesticQuestion
break
case StandardSource.OVERSEAS.value:
submitFunc = submitOverseasQuestion
break
default:
break
}
this.confirmLoading = true
const param = Object.assign({}, values)
submitFunc(param).then(res => {
if (res.success) {
this.$message.success(res.message)
this.close()
} else {
this.$message.warning(res.message)
}
}).finally(() => {
this.confirmLoading = false
})
}
})
},
handleCancel () {
this.close()
},
close () {
this.visible = false
},
filterOption (input, option) {
return option.componentOptions.children[0].text.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
}
}
</script>
<style scoped lang="less">
</style>

Some files were not shown because too many files have changed in this diff Show More