You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

830 lines
26 KiB

1 year ago
<template>
<div style="padding: 20px">
<el-card shadow="always">
<template #header>
<p>{{ title }}</p>
<vxe-toolbar ref="toolbar" custom print export>
1 year ago
<template #buttons>
<div class="selects">
9 months ago
<el-select
v-if="$route.params.type === 'all'"
style="width: 100px"
size="small"
v-model="select.department_id"
placeholder="部门..."
>
<el-option
v-for="item in departments"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
1 year ago
<el-select
style="width: 180px"
filterable
clearable
size="small"
v-model="select.custom_model_id"
>
<el-option
v-for="item in models"
:key="item.id"
:value="item.id"
:label="item.name"
></el-option>
</el-select>
<el-select
style="width: 100px"
size="small"
v-model="select.date_type"
>
<el-option
v-for="item in dataTypes"
:key="item.value"
:value="item.value"
:label="item.label"
></el-option>
</el-select>
<el-date-picker
style="width: 240px"
value-format="yyyy-MM-dd"
size="small"
:value="select.date_range ? select.date_range.split('~') : ''"
type="daterange"
align="right"
unlink-panels
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
:picker-options="pickerOptions"
@input="
([start, end]) => (select.date_range = `${start}~${end}`)
"
>
</el-date-picker>
<el-input
clearable
v-model="select.keyword"
style="width: 160px"
placeholder="请输入关键词"
size="small"
></el-input>
<el-button
icon="el-icon-search"
type="primary"
size="small"
10 months ago
@click="getList(true)"
1 year ago
>搜索</el-button
>
9 months ago
<el-button
v-if="select.custom_model_id"
icon="el-icon-download"
type="primary"
size="small"
@click="isShowFieldExport = true"
>导出</el-button
>
9 months ago
<!-- <el-button-->
<!-- v-if="$route.path === '/flow/list/todo'"-->
<!-- icon="el-icon-edit"-->
<!-- type="primary"-->
<!-- size="small"-->
<!-- plain-->
<!-- @click="multiDeal"-->
<!-- >批量审批</el-button-->
<!-- >-->
1 year ago
</div>
</template>
</vxe-toolbar>
11 months ago
<div class="todo-statistics" v-if="$route.path === '/flow/list/todo'">
<el-tag
v-for="(model, index) in todoTotal"
:key="model.custom_model_id"
:effect="select.custom_model_id === model.custom_model_id ? 'dark' : 'plain'"
:type="['success', 'info', 'warning', 'danger'][index%4]"
@click="select.custom_model_id = model.custom_model_id,getList()">
{{ model.custom_model ? model.custom_model.name : '' }}{{ model.total }}
</el-tag>
</div>
1 year ago
</template>
<template #default>
<div>
<vxe-table
ref="table"
stripe
style="margin-top: 10px"
:loading="loading"
keep-source
:column-config="{ resizable: true }"
:print-config="{}"
:export-config="{}"
1 year ago
:expand-config="{
accordion: true,
11 months ago
padding: true,
11 months ago
}"
:checkbox-config="{
reserve: true,
11 months ago
highlight: true,
range: true,
checkMethod: ({ row }) => {
if ($route.path !== '/flow/list/todo') {
return true
} else {
if (!select.custom_model_id) return false
if (multiDealFlows.length === 0) {
return true
} else {
const firstFlow = multiDealFlows[0]
return row.current_node.id === firstFlow.current_node.id
}
}
}
1 year ago
}"
11 months ago
:row-config="{ keyField: 'id' }"
1 year ago
:custom-config="{ mode: 'popup' }"
10 months ago
:menu-config="menuConfig"
1 year ago
:data="list"
11 months ago
@cell-click="cellClickEvent"
1 year ago
@cell-dblclick="cellDblclickEvent"
11 months ago
@checkbox-change="checkboxChange"
@checkbox-all="checkboxChange"
10 months ago
@menu-click="contextMenuClickEvent"
1 year ago
>
11 months ago
<vxe-column type="checkbox" width="50" align="center"></vxe-column>
11 months ago
<vxe-column :visible="$store.getters.device === 'mobile'" field="m-operate" type="expand" title="操作" width="60" align="center">
<template #content="{ row }">
<div>
9 months ago
<div class="mobile-desc">
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
流程名称
</div>
<div class="mobile-desc-row__value">
{{ row['custom_model'] ? row['custom_model']['name'] :'' }}
</div>
</div>
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
发起人
</div>
<div class="mobile-desc-row__value">
{{ row['creator'] ? row['creator']['name'] :'' }}
</div>
</div>
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
发起人
</div>
<div class="mobile-desc-row__value">
{{ (row['last_log'] && row['last_log']['user']) ? row['last_log']['user']['name'] :'' }}
</div>
</div>
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
当前节点
</div>
<div class="mobile-desc-row__value">
{{ (row['current_node']) ? row['current_node']['name'] :'' }}
</div>
</div>
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
简要
</div>
<div class="mobile-desc-row__value">
{{ row['_simple'] }}
</div>
</div>
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
发起日期
</div>
<div class="mobile-desc-row__value">
{{ row['created_at'] }}
</div>
</div>
<div class="mobile-desc-row">
<div class="mobile-desc-row__title">
当前状态
</div>
<div class="mobile-desc-row__value">
<el-tag
size="mini"
:type="statusColor.get(row.status)"
effect="dark"
>{{ myStatus.get(row.status) }}</el-tag
>
</div>
</div>
</div>
11 months ago
<template v-if="$route.params.type !== 'all'">
<el-button
v-if="row.my_log"
type="primary"
size="mini"
@click="handle(row)"
>办理</el-button
>
<el-button
plain
type="primary"
size="mini"
@click="copyTo(row)"
>抄送</el-button
>
<el-button
v-if="row.can_recall"
plain
type="danger"
size="mini"
@click="recall(row)"
>撤回</el-button
>
<el-button
11 months ago
v-if="row.can_delete || ($store.state.user.adminId === 1)"
11 months ago
plain
type="danger"
size="mini"
@click="destroy(row)"
>删除</el-button
>
</template>
<el-button plain type="success" size="mini" @click="detail(row)"
>查看</el-button
>
</div>
</template>
</vxe-column>
<vxe-column
10 months ago
min-width="220"
header-align="center"
field="title"
title="工作名称"
10 months ago
show-overflow
11 months ago
:title-prefix="{ content: '点击工作名称查看简要。\n批量审批请先选择流程类型并勾选同一节点的流程。' }"
></vxe-column>
<vxe-column
11 months ago
:visible="$route.params.type === 'all'"
width="80"
align="center"
field="id"
title="流水号"
></vxe-column>
<vxe-column
9 months ago
:visible="$store.getters.device === 'desktop'"
width="80"
title="发起人"
align="center"
field="creator.name"
></vxe-column>
<vxe-column
9 months ago
:visible="$route.params.type !== 'all' && $store.getters.device === 'desktop'"
width="80"
title="承办人员"
align="center"
field="last_log.user.name"
></vxe-column>
<vxe-column
9 months ago
:visible="$store.getters.device === 'desktop'"
10 months ago
align="center"
min-width="140"
field="custom_model.name"
title="流程名称"
></vxe-column>
10 months ago
<vxe-column
9 months ago
:visible="$store.getters.device === 'desktop'"
10 months ago
header-align="center"
min-width="240"
field="_simple"
title="简要"
></vxe-column>
<vxe-column
9 months ago
:visible="$route.params.type !== 'all' && $store.getters.device === 'desktop'"
align="center"
width="140"
field="current_node.name"
title="当前节点"
></vxe-column>
<vxe-column
9 months ago
:visible="$route.params.type !== 'all' && $store.getters.device === 'desktop'"
title="收藏状态"
width="90"
field="my_fav"
align="center"
>
<template #default="{ row }">
<el-button
:type="row.my_fav ? 'success' : 'primary'"
size="mini"
@click="toggleFav(row)"
>{{ row.my_fav ? "已收藏" : "未收藏" }}</el-button
>
</template>
</vxe-column>
<vxe-column
9 months ago
:visible="$store.getters.device === 'desktop'"
width="164"
field="no"
align="center"
title="编号"
></vxe-column>
<vxe-column
width="200"
9 months ago
:visible="$route.params.type === 'all' && $store.getters.device === 'desktop'"
align="center"
field="created_at"
title="发起日期"
:formatter="
({ cellValue }) =>
$moment(cellValue).format('YYYY-MM-DD HH:mm:ss')
"
:export-method="
({ row, column, options }) =>
$moment(row['created_at']).format('YYYY-MM-DD HH:mm:ss')
"
></vxe-column>
<vxe-column
9 months ago
:visible="$store.getters.device === 'desktop'"
width="80"
align="center"
field="status"
title="当前状态"
:formatter="({ cellValue }) => myStatus.get(cellValue)"
:export-method="
({ row, column, options }) => myStatus.get(row['status'])
"
>
<template #default="{ row }">
<el-tag
size="mini"
:type="statusColor.get(row.status)"
effect="dark"
>{{ myStatus.get(row.status) }}</el-tag
>
</template>
</vxe-column>
11 months ago
<vxe-column
:visible="$store.getters.device === 'desktop'"
min-width="280"
header-align="center"
field="operate"
title="操作"
fixed="right"
>
<template #default="{ row }">
<template v-if="$route.params.type !== 'all'">
<el-button
v-if="row.my_log"
type="primary"
size="mini"
@click="handle(row)"
>办理</el-button
>
<el-button
plain
type="primary"
size="mini"
@click="copyTo(row)"
>抄送</el-button
>
<el-button
v-if="row.can_recall"
plain
type="danger"
size="mini"
@click="recall(row)"
>撤回</el-button
>
<el-button
11 months ago
v-if="row.can_delete"
plain
type="danger"
size="mini"
@click="destroy(row)"
>删除</el-button
>
</template>
<template v-else>
<el-button
v-if="$store.state.user.adminId === 1"
11 months ago
plain
type="danger"
size="mini"
@click="destroy(row)"
>删除</el-button
1 year ago
>
</template>
11 months ago
<el-button plain type="success" size="mini" @click="detail(row)"
>查看</el-button
>
</template>
</vxe-column>
1 year ago
</vxe-table>
<el-pagination
style="margin-top: 10px"
@size-change="
(e) => {
select.page_size = e;
select.page = 1;
getList();
}
"
@current-change="
(e) => {
select.page = e;
getList();
}
"
1 year ago
:current-page.sync="select.page"
11 months ago
:page-sizes="[10, 20, 30, 50, 100]"
1 year ago
:page-size.sync="select.page_size"
9 months ago
:small="$store.getters.device === 'mobile'"
:layout="$store.getters.device === 'desktop' ? 'total, ->, prev, pager, next, sizes, jumper' : 'total, ->, prev, pager, next'"
1 year ago
:total="total"
></el-pagination>
</div>
</template>
</el-card>
<share ref="share" :is-show.sync="isShowShare" :flow="pickedFlow"></share>
11 months ago
<list-popover :is-show.sync="isShowListPopover" :id="listPopoverId" :pos="listPopoverPos" />
11 months ago
<multi-deal :is-show.sync="isShowMultiDeal" :deal-flows="multiDealFlows" @refresh="getList" />
9 months ago
<field-export :is-show.sync="isShowFieldExport" :select="select" />
1 year ago
</div>
</template>
<script>
11 months ago
import { flowList, toggleFav, destroy, recall, updateFlowTime, todoTotal } from "@/api/flow";
1 year ago
import moment from "moment/moment";
11 months ago
import ListPopover from "./components/ListPopover.vue";
import MultiDeal from "./components/MultiDeal.vue"
import share from "./components/share.vue";
9 months ago
import FieldExport from "./components/FieldExport.vue";
9 months ago
import { departmentListNoAuth } from "@/api/common"
1 year ago
export default {
12 months ago
name: "flowList",
components: {
11 months ago
share,
ListPopover,
9 months ago
MultiDeal,
FieldExport
},
1 year ago
data() {
return {
9 months ago
isShowFieldExport: false,
11 months ago
todoTotal: [],
11 months ago
isShowMultiDeal: false,
multiDealFlows: [],
isShowListPopover: false,
listPopoverId: "",
listPopoverPos: {
top: 100,
left: 100
},
myStatus: new Map([
[-1, "已退回"],
[0, "办理中"],
[1, "已完成"],
]),
statusColor: new Map([
[-1, "warning"],
[0, ""],
[1, "success"],
]),
1 year ago
pickerOptions: {
shortcuts: [
{
text: "一年前",
onClick(picker) {
picker.$emit("pick", [
moment().subtract(1, "years").toDate(),
new Date(),
]);
},
},
{
text: "一月前",
onClick(picker) {
picker.$emit("pick", [
moment().subtract(1, "months").toDate(),
new Date(),
]);
},
},
{
text: "一周前",
onClick(picker) {
picker.$emit("pick", [
moment().subtract(1, "weeks").toDate(),
new Date(),
]);
},
},
{
text: "一周后",
onClick(picker) {
picker.$emit("pick", [
new Date(),
moment().add(1, "weeks").toDate(),
]);
},
},
{
text: "一月后",
onClick(picker) {
picker.$emit("pick", [
new Date(),
moment().add(1, "months").toDate(),
]);
},
},
{
text: "一年后",
onClick(picker) {
picker.$emit("pick", [
new Date(),
moment().add(1, "years").toDate(),
]);
},
},
],
},
select: {
page: 1,
page_size: 20,
sort_name: "",
sort_type: "",
keyword: "",
department_id: "",
is_fav: "",
custom_model_id: "",
date_range: "",
date_type: "create_date",
},
loading: false,
list: [],
total: 0,
title: "",
models: [],
dataTypes: [
{
value: "create_date",
label: "录入时间",
},
{
value: "happened_date",
label: "经办时间",
},
],
9 months ago
departments: [],
isShowShare: false,
pickedFlow: {},
1 year ago
};
},
methods: {
9 months ago
async getDepartments() {
try {
9 months ago
this.departments = await departmentListNoAuth({
9 months ago
page: 1,
page_size: 9999
})
} catch (err) {
console.error(err)
}
},
10 months ago
contextMenuClickEvent({ menu, row, column }) {
switch (menu.code) {
case 'edit':
// 示例
if (row && column) {
const target = this.$router.resolve({
path: '/flow/edit',
query: {
flow_id: row.id
}
})
window.open(target.href, '_blank')
}
break
default:
}
},
10 months ago
contentFormatter(row) {
const { data, fields } = row
let text = ''
fields.forEach((field, index) => {
let options = []
if (field.show_in_list && field.type !== 'relation' && data[field.name]) {
if (field?.selection_model) {
options = field.selection_model_items || [];
}
text += `${field.label}:${(field?.selection_model ? options.find(j => j.id === data[field.name])?.name : data[field.name]) ?? '-'}\n`
}
row._simple = text
})
},
11 months ago
cellClickEvent(e) {
if (e.column?.field !== 'title') return
this.listPopoverId = e.row.id
this.listPopoverPos.top = e.$event.clientX
this.listPopoverPos.left = e.$event.clientY
this.isShowListPopover = true
},
handle(row) {
this.$router.push(
`/flow/create?module_id=${row.custom_model.id}&flow_id=${row.id}`
);
},
detail(row) {
this.$router.push(
`/flow/detail?module_id=${row.custom_model.id}&flow_id=${row.id}`
);
},
copyTo(row) {
this.pickedFlow = row;
this.isShowShare = true;
},
async toggleFav(row) {
try {
await toggleFav({
id: row.id,
});
await this.getList();
} catch (err) {}
},
async recall(row) {
try {
await recall({ id: row.id });
await this.getList();
} catch (err) {}
},
async destroy(row) {
11 months ago
if (row.can_delete || (this.$store.state.user.adminId === 1)) {
try {
await destroy({ id: row.id });
await this.getList();
} catch (err) {}
}
},
10 months ago
async getList(refresh=false) {
11 months ago
if (this.loading) return
1 year ago
this.loading = true;
try {
10 months ago
if (refresh) {
this.select.page = 1
}
const res = await flowList(this.$route.params.type, this.select, false);
10 months ago
res.data?.data?.forEach(i => this.contentFormatter(i))
1 year ago
this.list = res?.data?.data || [];
this.total = res?.data?.total ?? 0;
this.models = res.customModels;
this.title = res.pageTitle;
this.loading = false;
} catch (err) {
console.error(err);
this.loading = false;
}
},
1 year ago
async cellDblclickEvent({ row, column }) {
1 year ago
if(this.$store.state.user.username !== 'admin') return
1 year ago
if(column.field === 'created_at' || column.field === 'updated_at') {
1 year ago
this.$prompt('请输入时间', '提示', {
1 year ago
confirmButtonText: '确定',
cancelButtonText: '取消',
1 year ago
inputPattern: /^(?:19|20)[0-9][0-9]-(?:(?:0[1-9])|(?:1[0-2]))-(?:(?:[0-2][1-9])|(?:[1-3][0-1])) (?:(?:[0-2][0-3])|(?:[0-1][0-9])):[0-5][0-9]:[0-5][0-9]$/,
inputErrorMessage: '时间格式不正确'
}).then(({ value }) => {
updateFlowTime({
id: row.id,
date: value
}).then(_ => {
this.$message.success('更新成功')
this.getList()
})
1 year ago
})
}
},
11 months ago
11 months ago
checkboxChange() {
11 months ago
const reserveRecords = this.$refs['table']?.getCheckboxReserveRecords() || []
const records = this.$refs['table']?.getCheckboxRecords(true) || []
this.multiDealFlows = [...reserveRecords, ...records]
11 months ago
if (this.multiDealFlows.length > 0) {
const firstFlow = this.multiDealFlows[0]
this.multiDealFlows = this.multiDealFlows.filter(flow => {
if (flow.current_node.id === firstFlow.current_node.id) {
return true
} else {
this.$refs['table'].setCheckboxRow(flow, false)
}
})
}
},
async getTodoTotal() {
try {
const res = await todoTotal()
console.log(res)
this.todoTotal = res?.todo || []
} catch (err) {
console.error(err)
}
},
multiDeal() {
if (this.multiDealFlows.length === 0) {
this.$message.warning('请先勾选需要审批的流程')
return
}
11 months ago
this.isShowMultiDeal = true
}
1 year ago
},
1 year ago
computed: {
10 months ago
menuConfig() {
if (this.$store.state.user.adminId === 1) {
return {
className: 'my-menus',
body: {
options: [
[
{ code: 'edit', name: '编辑', prefixConfig: { icon: 'vxe-icon-feedback' } },
],
]
}
}
10 months ago
} else {
return {}
10 months ago
}
}
1 year ago
},
11 months ago
watch: {
'select.custom_model_id': {
handler: function () {
this.$refs['table'].clearCheckboxRow()
this.$refs['table'].clearCheckboxReserve()
this.multiDealFlows = []
}
}
},
12 months ago
activated() {
11 months ago
this.getTodoTotal();
12 months ago
this.getList();
},
1 year ago
created() {
9 months ago
this.getDepartments();
11 months ago
this.getTodoTotal();
1 year ago
this.getList();
},
mounted() {
this.$nextTick(() => {
if (this.$refs["table"] && this.$refs["toolbar"]) {
this.$refs["table"].connect(this.$refs["toolbar"]);
1 year ago
}
});
},
1 year ago
};
</script>
<style scoped lang="scss">
.selects > * + * {
margin: 2px 0 2px 6px;
}
11 months ago
.el-tag {
cursor: pointer;
}
.el-tag + .el-tag {
margin-left: 4px;
}
9 months ago
.mobile-desc {
&-row {
display: flex;
margin-bottom: 20px;
&__title {
flex-basis: 20vw;
font-weight: 600;
text-align: right;
}
&__value {
flex-basis: 52vw;
padding-left: 4vw;
}
}
}
1 year ago
</style>