|
|
|
|
@ -23,8 +23,20 @@
|
|
|
|
|
<!-- 搜索区域 -->
|
|
|
|
|
<div class="search-area" v-show="!collapsed && !readonly" @click.stop>
|
|
|
|
|
<el-row :gutter="10">
|
|
|
|
|
<el-col :span="4">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="relationCategory"
|
|
|
|
|
placeholder="关联类别"
|
|
|
|
|
style="width: 100%"
|
|
|
|
|
@change="handleCategoryChange"
|
|
|
|
|
>
|
|
|
|
|
<el-option label="流程" value="flow"></el-option>
|
|
|
|
|
<el-option label="会议纪要" value="meetingMinute"></el-option>
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
<el-select
|
|
|
|
|
v-if="relationCategory === 'flow'"
|
|
|
|
|
v-model="searchForm.custom_model_id"
|
|
|
|
|
placeholder="流程类型"
|
|
|
|
|
clearable
|
|
|
|
|
@ -46,7 +58,7 @@
|
|
|
|
|
<el-col :span="10">
|
|
|
|
|
<el-input
|
|
|
|
|
v-model="searchForm.keyword"
|
|
|
|
|
placeholder="输入标题或编号搜索"
|
|
|
|
|
:placeholder="relationCategory === 'flow' ? '输入标题或编号搜索' : '输入会议纪要标题搜索'"
|
|
|
|
|
clearable
|
|
|
|
|
@keyup.enter.native="handleSearch"
|
|
|
|
|
>
|
|
|
|
|
@ -86,8 +98,30 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="selected-flows" v-show="!collapsed && selectedMeetingMinutes.length > 0">
|
|
|
|
|
<div class="selected-title">已关联的会议纪要({{ selectedMeetingMinutes.length }})</div>
|
|
|
|
|
<div class="selected-list">
|
|
|
|
|
<div v-for="item in selectedMeetingMinutes" :key="`mm-${item.id}`" class="selected-flow-block">
|
|
|
|
|
<el-tag
|
|
|
|
|
:closable="!readonly"
|
|
|
|
|
@close="handleRemoveMeetingMinute(item.id)"
|
|
|
|
|
@click.native.stop="$emit('open-meeting-minute', item.id)"
|
|
|
|
|
class="flow-tag"
|
|
|
|
|
type="warning"
|
|
|
|
|
effect="plain"
|
|
|
|
|
>
|
|
|
|
|
<span class="flow-title">{{ item.title || ('会议纪要' + item.id) }}</span>
|
|
|
|
|
</el-tag>
|
|
|
|
|
<div class="flow-meeting-minutes">
|
|
|
|
|
<span class="mm-label">创建时间:</span>
|
|
|
|
|
<span>{{ formatDate(item.created_at) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 可选流程列表 -->
|
|
|
|
|
<div class="available-flows" v-show="!collapsed && !readonly">
|
|
|
|
|
<div class="available-flows" v-show="!collapsed && !readonly && relationCategory === 'flow'">
|
|
|
|
|
<div class="available-title">可选流程</div>
|
|
|
|
|
<div v-loading="loading" class="flow-list">
|
|
|
|
|
<el-empty v-if="!loading && availableFlows.length === 0" description="暂无可用流程"></el-empty>
|
|
|
|
|
@ -161,6 +195,68 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="available-flows" v-show="!collapsed && !readonly && relationCategory === 'meetingMinute'">
|
|
|
|
|
<div class="available-title">可选会议纪要</div>
|
|
|
|
|
<div v-loading="loading" class="flow-list">
|
|
|
|
|
<el-empty v-if="!loading && availableMeetingMinutes.length === 0" description="暂无可用会议纪要"></el-empty>
|
|
|
|
|
<div
|
|
|
|
|
v-for="item in availableMeetingMinutes"
|
|
|
|
|
:key="`meeting-minute-${item.id}`"
|
|
|
|
|
class="flow-item"
|
|
|
|
|
:class="{ 'is-selected': isMeetingMinuteSelected(item.id) }"
|
|
|
|
|
@click="handleToggleMeetingMinute(item)"
|
|
|
|
|
>
|
|
|
|
|
<div class="flow-content">
|
|
|
|
|
<div class="flow-header">
|
|
|
|
|
<span class="flow-title-text">{{ item.title || ('会议纪要' + item.id) }}</span>
|
|
|
|
|
<el-tag size="mini" type="warning">会议纪要</el-tag>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flow-info">
|
|
|
|
|
<span class="flow-no">ID:{{ item.id }}</span>
|
|
|
|
|
<span class="flow-date">{{ formatDate(item.created_at) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="flow-actions">
|
|
|
|
|
<el-button
|
|
|
|
|
size="mini"
|
|
|
|
|
type="text"
|
|
|
|
|
icon="el-icon-view"
|
|
|
|
|
@click.stop="$emit('open-meeting-minute', item.id)"
|
|
|
|
|
>
|
|
|
|
|
查看
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="!isMeetingMinuteSelected(item.id)"
|
|
|
|
|
size="mini"
|
|
|
|
|
type="primary"
|
|
|
|
|
@click.stop="handleToggleMeetingMinute(item)"
|
|
|
|
|
>
|
|
|
|
|
关联
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
v-else
|
|
|
|
|
size="mini"
|
|
|
|
|
type="danger"
|
|
|
|
|
@click.stop="handleToggleMeetingMinute(item)"
|
|
|
|
|
>
|
|
|
|
|
取消
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div class="pagination" v-if="meetingMinutePagination.total > 0">
|
|
|
|
|
<el-pagination
|
|
|
|
|
@current-change="handleMeetingMinutePageChange"
|
|
|
|
|
:current-page="meetingMinutePagination.page"
|
|
|
|
|
:page-size="meetingMinutePagination.page_size"
|
|
|
|
|
:total="meetingMinutePagination.total"
|
|
|
|
|
layout="total, prev, pager, next"
|
|
|
|
|
small
|
|
|
|
|
></el-pagination>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 只读模式显示关联流程 -->
|
|
|
|
|
<div class="readonly-flows" v-show="readonly">
|
|
|
|
|
<div v-if="relatedFlows.length > 0" class="related-section">
|
|
|
|
|
@ -203,8 +299,19 @@
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="readonly && relatedFlows.length === 0 && relatedByFlows.length === 0" class="empty-tip">
|
|
|
|
|
<span class="empty-text">暂无关联流程</span>
|
|
|
|
|
<div v-if="relatedMeetingMinutes.length > 0" class="related-section">
|
|
|
|
|
<div class="section-title">关联的会议纪要({{ relatedMeetingMinutes.length }})</div>
|
|
|
|
|
<div class="related-list">
|
|
|
|
|
<div v-for="item in relatedMeetingMinutes" :key="`readonly-mm-${item.id}`" class="related-item">
|
|
|
|
|
<div class="related-item-flow" @click="$emit('open-meeting-minute', item.id)">
|
|
|
|
|
<span class="flow-title-text">{{ item.title || ('会议纪要' + item.id) }}</span>
|
|
|
|
|
<span class="flow-meta">(ID: {{ item.id }})</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="readonly && relatedFlows.length === 0 && relatedByFlows.length === 0 && relatedMeetingMinutes.length === 0" class="empty-tip">
|
|
|
|
|
<span class="empty-text">暂无关联项</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-card>
|
|
|
|
|
@ -228,7 +335,7 @@
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import { getRelationOptions, getRelationList, flowList, flow as flowIndex } from '@/api/flow'
|
|
|
|
|
import { getRelationMeetingMinuteOptions, getRelationOptions, getRelationList, flowList, flow as flowIndex } from '@/api/flow'
|
|
|
|
|
import { getToken } from '@/utils/auth'
|
|
|
|
|
import moment from 'moment'
|
|
|
|
|
|
|
|
|
|
@ -247,6 +354,10 @@ export default {
|
|
|
|
|
type: [Number, String],
|
|
|
|
|
default: null
|
|
|
|
|
},
|
|
|
|
|
meetingMinuteValue: {
|
|
|
|
|
type: [String, Array],
|
|
|
|
|
default: () => []
|
|
|
|
|
},
|
|
|
|
|
collapsible: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true
|
|
|
|
|
@ -256,20 +367,29 @@ export default {
|
|
|
|
|
return {
|
|
|
|
|
collapsed: this.readonly ? false : true, // readonly模式默认展开,编辑模式默认收起
|
|
|
|
|
loading: false,
|
|
|
|
|
relationCategory: 'flow',
|
|
|
|
|
searchForm: {
|
|
|
|
|
keyword: '',
|
|
|
|
|
custom_model_id: ''
|
|
|
|
|
},
|
|
|
|
|
availableFlows: [],
|
|
|
|
|
availableMeetingMinutes: [],
|
|
|
|
|
selectedFlows: [],
|
|
|
|
|
selectedMeetingMinutes: [],
|
|
|
|
|
relatedFlows: [],
|
|
|
|
|
relatedByFlows: [],
|
|
|
|
|
relatedMeetingMinutes: [],
|
|
|
|
|
customModels: [],
|
|
|
|
|
pagination: {
|
|
|
|
|
page: 1,
|
|
|
|
|
page_size: 20,
|
|
|
|
|
total: 0
|
|
|
|
|
},
|
|
|
|
|
meetingMinutePagination: {
|
|
|
|
|
page: 1,
|
|
|
|
|
page_size: 20,
|
|
|
|
|
total: 0
|
|
|
|
|
},
|
|
|
|
|
showFlowDetail: false,
|
|
|
|
|
currentFlow: null,
|
|
|
|
|
flowDetailUrl: ''
|
|
|
|
|
@ -290,6 +410,19 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
meetingMinuteValue: {
|
|
|
|
|
immediate: true,
|
|
|
|
|
handler(newVal) {
|
|
|
|
|
if (newVal) {
|
|
|
|
|
const ids = Array.isArray(newVal) ? newVal : newVal.toString().split(',').filter(Boolean).map(Number)
|
|
|
|
|
if (ids.length > 0 && this.selectedMeetingMinutes.length === 0) {
|
|
|
|
|
this.loadSelectedMeetingMinutes(ids)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this.selectedMeetingMinutes = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
readonly: {
|
|
|
|
|
immediate: true,
|
|
|
|
|
handler(newVal) {
|
|
|
|
|
@ -322,7 +455,17 @@ export default {
|
|
|
|
|
mounted() {
|
|
|
|
|
this.loadCustomModels()
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
methods: {
|
|
|
|
|
handleCategoryChange() {
|
|
|
|
|
this.searchForm.keyword = ''
|
|
|
|
|
this.searchForm.custom_model_id = ''
|
|
|
|
|
this.pagination.page = 1
|
|
|
|
|
this.meetingMinutePagination.page = 1
|
|
|
|
|
if (this.collapsed) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
this.handleSearch()
|
|
|
|
|
},
|
|
|
|
|
handleToggleCollapse(e) {
|
|
|
|
|
if (e) {
|
|
|
|
|
e.stopPropagation()
|
|
|
|
|
@ -347,10 +490,10 @@ export default {
|
|
|
|
|
const res = await flowList('all', { page: 1, page_size: 1, is_simple: 1 })
|
|
|
|
|
// customModels 在 res 的顶层,不在 res.data 中
|
|
|
|
|
if (res.customModels && res.customModels.length > 0) {
|
|
|
|
|
this.customModels = res.customModels
|
|
|
|
|
this.customModels = (res.customModels || []).filter(model => !model.url)
|
|
|
|
|
return
|
|
|
|
|
} else if (res.data && res.data.customModels) {
|
|
|
|
|
this.customModels = res.data.customModels
|
|
|
|
|
this.customModels = (res.data.customModels || []).filter(model => !model.url)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
@ -369,7 +512,7 @@ export default {
|
|
|
|
|
models.push(...cate.customerModels)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
this.customModels = models.map(m => ({ id: m.id, name: m.name }))
|
|
|
|
|
this.customModels = models.filter(m => !m.url).map(m => ({ id: m.id, name: m.name, url: m.url }))
|
|
|
|
|
}
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.error('备用加载流程类型失败', e)
|
|
|
|
|
@ -377,6 +520,9 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async handleSearch() {
|
|
|
|
|
if (this.relationCategory === 'meetingMinute') {
|
|
|
|
|
return this.handleMeetingMinuteSearch()
|
|
|
|
|
}
|
|
|
|
|
this.loading = true
|
|
|
|
|
try {
|
|
|
|
|
const params = {
|
|
|
|
|
@ -431,6 +577,40 @@ export default {
|
|
|
|
|
console.error('加载已选流程失败', err)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async handleMeetingMinuteSearch() {
|
|
|
|
|
this.loading = true
|
|
|
|
|
try {
|
|
|
|
|
const res = await getRelationMeetingMinuteOptions({
|
|
|
|
|
page: this.meetingMinutePagination.page,
|
|
|
|
|
page_size: this.meetingMinutePagination.page_size,
|
|
|
|
|
keyword: this.searchForm.keyword
|
|
|
|
|
})
|
|
|
|
|
if (res) {
|
|
|
|
|
this.availableMeetingMinutes = res.data || []
|
|
|
|
|
this.meetingMinutePagination.total = res.total || 0
|
|
|
|
|
this.meetingMinutePagination.page = res.page || 1
|
|
|
|
|
this.meetingMinutePagination.page_size = res.page_size || 20
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
this.$message.error('搜索失败:' + (err.message || '未知错误'))
|
|
|
|
|
} finally {
|
|
|
|
|
this.loading = false
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async loadSelectedMeetingMinutes(ids) {
|
|
|
|
|
if (!ids || ids.length === 0) {
|
|
|
|
|
this.selectedMeetingMinutes = []
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
const res = await getRelationMeetingMinuteOptions({
|
|
|
|
|
ids: ids.join(',')
|
|
|
|
|
})
|
|
|
|
|
this.selectedMeetingMinutes = res.data || []
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('加载已选会议纪要失败', err)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
async loadRelationList() {
|
|
|
|
|
if (!this.flowId) return
|
|
|
|
|
try {
|
|
|
|
|
@ -439,6 +619,7 @@ export default {
|
|
|
|
|
if (res) {
|
|
|
|
|
this.relatedFlows = res.related_flows || []
|
|
|
|
|
this.relatedByFlows = res.related_by_flows || []
|
|
|
|
|
this.relatedMeetingMinutes = res.related_meeting_minutes || []
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error('加载关联列表失败', err)
|
|
|
|
|
@ -460,18 +641,42 @@ export default {
|
|
|
|
|
this.selectedFlows = this.selectedFlows.filter(f => f.id !== flowId)
|
|
|
|
|
this.updateValue()
|
|
|
|
|
},
|
|
|
|
|
handleToggleMeetingMinute(item) {
|
|
|
|
|
if (this.isMeetingMinuteSelected(item.id)) {
|
|
|
|
|
this.handleRemoveMeetingMinute(item.id)
|
|
|
|
|
} else {
|
|
|
|
|
this.selectedMeetingMinutes.push(item)
|
|
|
|
|
this.updateMeetingMinuteValue()
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
handleRemoveMeetingMinute(id) {
|
|
|
|
|
this.selectedMeetingMinutes = this.selectedMeetingMinutes.filter(item => item.id !== id)
|
|
|
|
|
this.updateMeetingMinuteValue()
|
|
|
|
|
},
|
|
|
|
|
isSelected(flowId) {
|
|
|
|
|
return this.selectedFlows.some(f => f.id === flowId)
|
|
|
|
|
},
|
|
|
|
|
isMeetingMinuteSelected(id) {
|
|
|
|
|
return this.selectedMeetingMinutes.some(item => item.id === id)
|
|
|
|
|
},
|
|
|
|
|
updateValue() {
|
|
|
|
|
const ids = this.selectedFlows.map(f => f.id)
|
|
|
|
|
this.$emit('input', ids.join(','))
|
|
|
|
|
this.$emit('change', ids)
|
|
|
|
|
},
|
|
|
|
|
updateMeetingMinuteValue() {
|
|
|
|
|
const ids = this.selectedMeetingMinutes.map(item => item.id)
|
|
|
|
|
this.$emit('update:meetingMinuteValue', ids.join(','))
|
|
|
|
|
this.$emit('meeting-minute-change', ids)
|
|
|
|
|
},
|
|
|
|
|
handlePageChange(page) {
|
|
|
|
|
this.pagination.page = page
|
|
|
|
|
this.handleSearch()
|
|
|
|
|
},
|
|
|
|
|
handleMeetingMinutePageChange(page) {
|
|
|
|
|
this.meetingMinutePagination.page = page
|
|
|
|
|
this.handleMeetingMinuteSearch()
|
|
|
|
|
},
|
|
|
|
|
handleViewFlow(flow) {
|
|
|
|
|
this.currentFlow = flow
|
|
|
|
|
const baseUrl = process.env.VUE_APP_BASE_API || ''
|
|
|
|
|
@ -799,4 +1004,3 @@ export default {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
|