添加顶部导航,添加采购与合同审批,另外添加采购流合同流说明
This commit is contained in:
43
ruoyi-ui/src/api/oa/approval.js
Normal file
43
ruoyi-ui/src/api/oa/approval.js
Normal file
@@ -0,0 +1,43 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// ===== 审批配置 =====
|
||||
export function listApprovalConfig() {
|
||||
return request({ url: '/oa/approval/config/list', method: 'get' })
|
||||
}
|
||||
export function getApprovalConfig(businessType) {
|
||||
return request({ url: '/oa/approval/config/' + businessType, method: 'get' })
|
||||
}
|
||||
export function saveApprovalConfig(data) {
|
||||
return request({ url: '/oa/approval/config', method: 'post', data })
|
||||
}
|
||||
export function delApprovalConfig(id) {
|
||||
return request({ url: '/oa/approval/config/' + id, method: 'delete' })
|
||||
}
|
||||
|
||||
// ===== 我的审批 =====
|
||||
export function listMyPending(query) {
|
||||
return request({ url: '/oa/approval/mine/pending', method: 'get', params: query })
|
||||
}
|
||||
export function listMyDone(query) {
|
||||
return request({ url: '/oa/approval/mine/done', method: 'get', params: query })
|
||||
}
|
||||
export function listMySubmitted(query) {
|
||||
return request({ url: '/oa/approval/mine/submitted', method: 'get', params: query })
|
||||
}
|
||||
export function listAllApproval(query) {
|
||||
return request({ url: '/oa/approval/list', method: 'get', params: query })
|
||||
}
|
||||
|
||||
// ===== 操作 =====
|
||||
export function actApproval(data) {
|
||||
return request({ url: '/oa/approval/act', method: 'post', data })
|
||||
}
|
||||
export function withdrawApproval(instanceId) {
|
||||
return request({ url: '/oa/approval/withdraw/' + instanceId, method: 'post' })
|
||||
}
|
||||
export function getApprovalDetail(instanceId) {
|
||||
return request({ url: '/oa/approval/detail/' + instanceId, method: 'get' })
|
||||
}
|
||||
export function getLatestApproval(businessType, businessId) {
|
||||
return request({ url: '/oa/approval/latest', method: 'get', params: { businessType, businessId } })
|
||||
}
|
||||
@@ -3,8 +3,8 @@
|
||||
<hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container"
|
||||
@toggleClick="toggleSideBar" />
|
||||
|
||||
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" v-if="!topNav" />
|
||||
<top-nav id="topmenu-container" class="topmenu-container" v-if="topNav" />
|
||||
<sub-menu-bar v-if="!topNav" />
|
||||
|
||||
<div class="right-menu">
|
||||
<template v-if="device !== 'mobile'">
|
||||
@@ -51,6 +51,7 @@ import RuoYiGit from "@/components/RuoYi/Git";
|
||||
import Screenfull from "@/components/Screenfull";
|
||||
import SizeSelect from "@/components/SizeSelect";
|
||||
import TopNav from "@/components/TopNav";
|
||||
import SubMenuBar from "@/layout/components/SubMenuBar/index.vue";
|
||||
import AIChat from "@/layout/components/AIChat/index.vue";
|
||||
import FeedbackEntry from "@/layout/components/FeedbackEntry.vue";
|
||||
import { parseTime } from "@/utils/ruoyi";
|
||||
@@ -65,6 +66,7 @@ export default {
|
||||
// ChatComponent,
|
||||
Breadcrumb,
|
||||
TopNav,
|
||||
SubMenuBar,
|
||||
Hamburger,
|
||||
Screenfull,
|
||||
SizeSelect,
|
||||
|
||||
95
ruoyi-ui/src/layout/components/SubMenuBar/index.vue
Normal file
95
ruoyi-ui/src/layout/components/SubMenuBar/index.vue
Normal file
@@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div class="sub-menu-bar" v-if="items.length">
|
||||
<router-link
|
||||
v-for="item in items"
|
||||
:key="item.path"
|
||||
:to="item.path"
|
||||
class="sub-menu-item"
|
||||
:class="{ active: isActive(item) }"
|
||||
>{{ item.title }}</router-link>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "SubMenuBar",
|
||||
computed: {
|
||||
...mapGetters(["sidebarRouters"]),
|
||||
activeTopPath () {
|
||||
const path = this.$route.path || "";
|
||||
if (!path || path === "/") return "";
|
||||
const seg = path.split("/").filter(Boolean)[0];
|
||||
return seg ? "/" + seg : "";
|
||||
},
|
||||
items () {
|
||||
const top = this.findTop(this.sidebarRouters, this.activeTopPath);
|
||||
if (!top) return [];
|
||||
const children = (top.children || []).filter(c => !c.hidden);
|
||||
return children.map(c => ({
|
||||
path: this.resolvePath(top.path, c.path),
|
||||
title: c.meta?.title
|
||||
})).filter(it => it.title);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
findTop (routes, activePath) {
|
||||
if (!routes || !activePath) return null;
|
||||
for (const r of routes) {
|
||||
if (r.hidden) continue;
|
||||
const rp = r.path?.startsWith("/") ? r.path : "/" + r.path;
|
||||
if (rp === activePath) return r;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
resolvePath (parent, child) {
|
||||
if (!child) return parent;
|
||||
if (/^https?:\/\//.test(child)) return child;
|
||||
if (child.startsWith("/")) return child;
|
||||
return (parent || "").replace(/\/+$/, "") + "/" + child;
|
||||
},
|
||||
isActive (item) {
|
||||
return this.$route.path === item.path || this.$route.path.startsWith(item.path + "/");
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.sub-menu-bar {
|
||||
float: left;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding-left: 12px;
|
||||
overflow-x: auto;
|
||||
max-width: calc(100% - 360px);
|
||||
}
|
||||
|
||||
.sub-menu-bar::-webkit-scrollbar {
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.sub-menu-item {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
padding: 0 16px;
|
||||
font-size: 13px;
|
||||
color: #5a5e66;
|
||||
white-space: nowrap;
|
||||
text-decoration: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
transition: color 0.15s;
|
||||
}
|
||||
|
||||
.sub-menu-item:hover {
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.sub-menu-item.active {
|
||||
color: #409eff;
|
||||
border-bottom-color: #409eff;
|
||||
}
|
||||
</style>
|
||||
169
ruoyi-ui/src/views/oa/approval/config/index.vue
Normal file
169
ruoyi-ui/src/views/oa/approval/config/index.vue
Normal file
@@ -0,0 +1,169 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-alert
|
||||
title="配置每个业务的审批人和或签/会签规则。审批人 user_id 多选;保存后立即生效,但只影响新提交的审批单。"
|
||||
type="info" show-icon :closable="false" style="margin-bottom: 12px;" />
|
||||
|
||||
<div style="margin-bottom: 10px;">
|
||||
<el-button type="primary" icon="el-icon-plus" size="mini" @click="handleAdd">新增业务配置</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="getList">刷新</el-button>
|
||||
</div>
|
||||
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="业务类型 key" prop="businessType" width="180" />
|
||||
<el-table-column label="业务名称" prop="businessName" width="180" />
|
||||
<el-table-column label="审批人" prop="approverNames" min-width="200" show-overflow-tooltip>
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-for="(name, i) in (row.approverNames || '').split(',').filter(Boolean)"
|
||||
:key="i" size="mini" style="margin-right: 4px;">{{ name }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="规则" prop="signType" width="100" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.signType === 1" type="success" size="mini">或签</el-tag>
|
||||
<el-tag v-else type="warning" size="mini">会签</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用" prop="enabled" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.enabled === 1" size="mini" type="success">启用</el-tag>
|
||||
<el-tag v-else size="mini" type="info">停用</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="备注" prop="remark" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleEdit(row)">改审批人</el-button>
|
||||
<el-button size="mini" type="text" icon="el-icon-delete" style="color:#f56c6c" @click="handleDel(row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog :title="form.id ? '修改审批配置' : '新增审批配置'" :visible.sync="open" width="640px" append-to-body>
|
||||
<el-form ref="form" :model="form" :rules="rules" label-width="120px" size="small">
|
||||
<el-form-item label="业务类型 key" prop="businessType">
|
||||
<el-input v-model="form.businessType" :disabled="!!form.id" placeholder="如 purchase_req / contract" />
|
||||
<div class="form-tip">代码内调用 approvalService.submit 时使用的 key,建表后不要修改。</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="业务名称" prop="businessName">
|
||||
<el-input v-model="form.businessName" placeholder="如 采购需求" />
|
||||
</el-form-item>
|
||||
<el-form-item label="审批人" prop="approverIdList">
|
||||
<el-select v-model="form.approverIdList" multiple filterable
|
||||
placeholder="选择一个或多个审批人" style="width: 100%">
|
||||
<el-option v-for="u in userOptions" :key="u.userId"
|
||||
:label="u.nickName + (u.userName ? ('('+u.userName+')') : '')" :value="u.userId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="规则" prop="signType">
|
||||
<el-radio-group v-model="form.signType">
|
||||
<el-radio :label="1">或签(任一人通过即通过)</el-radio>
|
||||
<el-radio :label="2">会签(全部通过才通过)</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="启用" prop="enabled">
|
||||
<el-switch v-model="form.enabled" :active-value="1" :inactive-value="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div slot="footer">
|
||||
<el-button @click="open = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit">保 存</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { listApprovalConfig, saveApprovalConfig, delApprovalConfig } from '@/api/oa/approval'
|
||||
import { listUser } from '@/api/system/user'
|
||||
|
||||
export default {
|
||||
name: 'OaApprovalConfig',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
list: [],
|
||||
userOptions: [],
|
||||
open: false,
|
||||
form: this.emptyForm(),
|
||||
rules: {
|
||||
businessType: [{ required: true, message: '业务类型不能为空', trigger: 'blur' }],
|
||||
businessName: [{ required: true, message: '业务名称不能为空', trigger: 'blur' }],
|
||||
approverIdList: [{ required: true, type: 'array', min: 1, message: '至少选择一个审批人', trigger: 'change' }],
|
||||
signType: [{ required: true, message: '请选择规则', trigger: 'change' }]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
this.loadUsers()
|
||||
},
|
||||
methods: {
|
||||
emptyForm() {
|
||||
return { id: null, businessType: '', businessName: '', approverIdList: [], signType: 1, enabled: 1, remark: '' }
|
||||
},
|
||||
getList() {
|
||||
this.loading = true
|
||||
listApprovalConfig().then(res => {
|
||||
this.list = res.data || []
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
loadUsers() {
|
||||
listUser({ pageNum: 1, pageSize: 1000, status: '0' }).then(res => {
|
||||
this.userOptions = res.rows || []
|
||||
})
|
||||
},
|
||||
handleAdd() {
|
||||
this.form = this.emptyForm()
|
||||
this.open = true
|
||||
},
|
||||
handleEdit(row) {
|
||||
const ids = (row.approverIds || '').split(',').filter(Boolean).map(s => Number(s))
|
||||
this.form = {
|
||||
id: row.id,
|
||||
businessType: row.businessType,
|
||||
businessName: row.businessName,
|
||||
approverIdList: ids,
|
||||
signType: row.signType,
|
||||
enabled: row.enabled,
|
||||
remark: row.remark
|
||||
}
|
||||
this.open = true
|
||||
},
|
||||
submit() {
|
||||
this.$refs.form.validate(valid => {
|
||||
if (!valid) return
|
||||
const payload = {
|
||||
id: this.form.id,
|
||||
businessType: this.form.businessType,
|
||||
businessName: this.form.businessName,
|
||||
approverIds: (this.form.approverIdList || []).join(','),
|
||||
signType: this.form.signType,
|
||||
enabled: this.form.enabled,
|
||||
remark: this.form.remark
|
||||
}
|
||||
saveApprovalConfig(payload).then(() => {
|
||||
this.$modal.msgSuccess('保存成功')
|
||||
this.open = false
|
||||
this.getList()
|
||||
})
|
||||
})
|
||||
},
|
||||
handleDel(row) {
|
||||
this.$modal.confirm('确认删除业务【' + row.businessName + '】的审批配置?').then(() =>
|
||||
delApprovalConfig(row.id)
|
||||
).then(() => {
|
||||
this.$modal.msgSuccess('删除成功')
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-tip { color: #909399; font-size: 12px; line-height: 1.4; margin-top: 4px; }
|
||||
</style>
|
||||
168
ruoyi-ui/src/views/oa/approval/pending/index.vue
Normal file
168
ruoyi-ui/src/views/oa/approval/pending/index.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-tabs v-model="tab" @tab-click="onTabClick" class="compact-tabs">
|
||||
<el-tab-pane name="pending" label="待我审批" />
|
||||
<el-tab-pane name="done" label="我已审批" />
|
||||
</el-tabs>
|
||||
|
||||
<el-form :model="queryParams" size="mini" :inline="true" class="compact-search">
|
||||
<el-form-item label="业务类型">
|
||||
<el-select v-model="queryParams.businessType" clearable placeholder="全部" style="width: 200px;">
|
||||
<el-option v-for="c in configs" :key="c.businessType"
|
||||
:label="c.businessName + '(' + c.businessType + ')'" :value="c.businessType" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="业务" width="160">
|
||||
<template slot-scope="{ row }">
|
||||
{{ row.businessName || row.businessType }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标题" prop="businessTitle" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="申请人" prop="applyUserName" width="100" />
|
||||
<el-table-column label="申请时间" prop="applyTime" width="160" />
|
||||
<el-table-column label="规则" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag size="mini" :type="row.signType === 1 ? 'success' : 'warning'">
|
||||
{{ row.signType === 1 ? '或签' : '会签' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批人" prop="approverNames" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag size="mini" :type="statusTag(row.status)">{{ statusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="200" align="center" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button v-if="tab === 'pending'" type="success" size="mini" @click="openAct(row, 1)">通过</el-button>
|
||||
<el-button v-if="tab === 'pending'" type="danger" size="mini" @click="openAct(row, 2)">驳回</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-document" @click="openDetail(row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total"
|
||||
:page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<!-- 审批操作弹窗 -->
|
||||
<el-dialog :title="actForm.action === 1 ? '审批通过' : '审批驳回'" :visible.sync="actOpen" width="500px" append-to-body>
|
||||
<div style="margin-bottom: 10px;">
|
||||
<b>{{ actForm.businessName }} - {{ actForm.businessTitle }}</b>
|
||||
</div>
|
||||
<el-input v-model="actForm.comment" type="textarea" :rows="3" placeholder="意见(可选)" />
|
||||
<div slot="footer">
|
||||
<el-button @click="actOpen = false">取消</el-button>
|
||||
<el-button :type="actForm.action === 1 ? 'success' : 'danger'" @click="doAct">
|
||||
{{ actForm.action === 1 ? '确认通过' : '确认驳回' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<el-dialog title="审批详情" :visible.sync="detailOpen" width="640px" append-to-body>
|
||||
<div v-if="detail">
|
||||
<p><b>业务:</b>{{ detail.businessName || detail.businessType }} #{{ detail.businessId }}</p>
|
||||
<p><b>标题:</b>{{ detail.businessTitle }}</p>
|
||||
<p><b>申请人:</b>{{ detail.applyUserName }}({{ detail.applyTime }})</p>
|
||||
<p><b>审批人:</b>{{ detail.approverNames }}({{ detail.signType === 1 ? '或签' : '会签' }})</p>
|
||||
<p><b>状态:</b>
|
||||
<el-tag size="mini" :type="statusTag(detail.status)">{{ statusText(detail.status) }}</el-tag>
|
||||
</p>
|
||||
<el-divider>审批流水</el-divider>
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="r in (detail.records || [])" :key="r.id"
|
||||
:type="r.action === 1 ? 'success' : 'danger'"
|
||||
:timestamp="r.opTime">
|
||||
<b>{{ r.approverName }}</b> {{ r.action === 1 ? '通过' : '驳回' }}
|
||||
<div v-if="r.comment" style="color:#666;">{{ r.comment }}</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listMyPending, listMyDone, listApprovalConfig,
|
||||
actApproval, getApprovalDetail
|
||||
} from '@/api/oa/approval'
|
||||
|
||||
export default {
|
||||
name: 'OaApprovalPending',
|
||||
data() {
|
||||
return {
|
||||
tab: 'pending',
|
||||
loading: false,
|
||||
total: 0,
|
||||
list: [],
|
||||
configs: [],
|
||||
queryParams: { pageNum: 1, pageSize: 10, businessType: undefined },
|
||||
actOpen: false,
|
||||
actForm: { instanceId: null, action: 1, comment: '', businessName: '', businessTitle: '' },
|
||||
detailOpen: false,
|
||||
detail: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
listApprovalConfig().then(res => { this.configs = res.data || [] })
|
||||
},
|
||||
methods: {
|
||||
statusText(s) {
|
||||
return ({ 0: '待审', 1: '通过', 2: '驳回', 3: '撤回' })[s] || '-'
|
||||
},
|
||||
statusTag(s) {
|
||||
return ({ 0: 'warning', 1: 'success', 2: 'danger', 3: 'info' })[s] || ''
|
||||
},
|
||||
onTabClick() {
|
||||
this.queryParams.pageNum = 1
|
||||
this.getList()
|
||||
},
|
||||
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
|
||||
reset() { this.queryParams = { pageNum: 1, pageSize: 10, businessType: undefined }; this.getList() },
|
||||
getList() {
|
||||
this.loading = true
|
||||
const fn = this.tab === 'pending' ? listMyPending : listMyDone
|
||||
fn(this.queryParams).then(res => {
|
||||
this.list = res.rows || []
|
||||
this.total = res.total || 0
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
openAct(row, action) {
|
||||
this.actForm = {
|
||||
instanceId: row.id, action, comment: '',
|
||||
businessName: row.businessName || row.businessType,
|
||||
businessTitle: row.businessTitle
|
||||
}
|
||||
this.actOpen = true
|
||||
},
|
||||
doAct() {
|
||||
actApproval({
|
||||
instanceId: this.actForm.instanceId,
|
||||
action: this.actForm.action,
|
||||
comment: this.actForm.comment
|
||||
}).then(() => {
|
||||
this.$modal.msgSuccess('已提交')
|
||||
this.actOpen = false
|
||||
this.getList()
|
||||
})
|
||||
},
|
||||
openDetail(row) {
|
||||
getApprovalDetail(row.id).then(res => {
|
||||
this.detail = res.data
|
||||
this.detailOpen = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
112
ruoyi-ui/src/views/oa/approval/submitted/index.vue
Normal file
112
ruoyi-ui/src/views/oa/approval/submitted/index.vue
Normal file
@@ -0,0 +1,112 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-form :model="queryParams" size="mini" :inline="true" class="compact-search">
|
||||
<el-form-item label="业务类型">
|
||||
<el-select v-model="queryParams.businessType" clearable placeholder="全部" style="width: 200px;">
|
||||
<el-option v-for="c in configs" :key="c.businessType"
|
||||
:label="c.businessName + '(' + c.businessType + ')'" :value="c.businessType" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
||||
<el-button icon="el-icon-refresh" size="mini" @click="reset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="业务" width="160">
|
||||
<template slot-scope="{ row }">{{ row.businessName || row.businessType }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="标题" prop="businessTitle" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="申请时间" prop="applyTime" width="160" />
|
||||
<el-table-column label="审批人" prop="approverNames" min-width="160" show-overflow-tooltip />
|
||||
<el-table-column label="状态" width="80" align="center">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag size="mini" :type="statusTag(row.status)">{{ statusText(row.status) }}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="完成时间" prop="finishTime" width="160" />
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template slot-scope="{ row }">
|
||||
<el-button v-if="row.status === 0" type="text" size="mini" style="color:#f56c6c"
|
||||
@click="withdraw(row)">撤回</el-button>
|
||||
<el-button type="text" size="mini" icon="el-icon-document" @click="openDetail(row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination v-show="total > 0" :total="total"
|
||||
:page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" />
|
||||
|
||||
<el-dialog title="审批详情" :visible.sync="detailOpen" width="640px" append-to-body>
|
||||
<div v-if="detail">
|
||||
<p><b>业务:</b>{{ detail.businessName || detail.businessType }} #{{ detail.businessId }}</p>
|
||||
<p><b>标题:</b>{{ detail.businessTitle }}</p>
|
||||
<p><b>申请人:</b>{{ detail.applyUserName }}({{ detail.applyTime }})</p>
|
||||
<p><b>审批人:</b>{{ detail.approverNames }}({{ detail.signType === 1 ? '或签' : '会签' }})</p>
|
||||
<p><b>状态:</b>
|
||||
<el-tag size="mini" :type="statusTag(detail.status)">{{ statusText(detail.status) }}</el-tag>
|
||||
</p>
|
||||
<el-divider>审批流水</el-divider>
|
||||
<el-timeline>
|
||||
<el-timeline-item v-for="r in (detail.records || [])" :key="r.id"
|
||||
:type="r.action === 1 ? 'success' : 'danger'"
|
||||
:timestamp="r.opTime">
|
||||
<b>{{ r.approverName }}</b> {{ r.action === 1 ? '通过' : '驳回' }}
|
||||
<div v-if="r.comment" style="color:#666;">{{ r.comment }}</div>
|
||||
</el-timeline-item>
|
||||
</el-timeline>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
listMySubmitted, listApprovalConfig,
|
||||
withdrawApproval, getApprovalDetail
|
||||
} from '@/api/oa/approval'
|
||||
|
||||
export default {
|
||||
name: 'OaApprovalSubmitted',
|
||||
data() {
|
||||
return {
|
||||
loading: false, total: 0, list: [], configs: [],
|
||||
queryParams: { pageNum: 1, pageSize: 10, businessType: undefined },
|
||||
detailOpen: false, detail: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getList()
|
||||
listApprovalConfig().then(res => { this.configs = res.data || [] })
|
||||
},
|
||||
methods: {
|
||||
statusText(s) { return ({ 0: '待审', 1: '通过', 2: '驳回', 3: '撤回' })[s] || '-' },
|
||||
statusTag(s) { return ({ 0: 'warning', 1: 'success', 2: 'danger', 3: 'info' })[s] || '' },
|
||||
handleQuery() { this.queryParams.pageNum = 1; this.getList() },
|
||||
reset() { this.queryParams = { pageNum: 1, pageSize: 10, businessType: undefined }; this.getList() },
|
||||
getList() {
|
||||
this.loading = true
|
||||
listMySubmitted(this.queryParams).then(res => {
|
||||
this.list = res.rows || []
|
||||
this.total = res.total || 0
|
||||
}).finally(() => { this.loading = false })
|
||||
},
|
||||
withdraw(row) {
|
||||
this.$modal.confirm('确认撤回审批单?').then(() =>
|
||||
withdrawApproval(row.id)
|
||||
).then(() => {
|
||||
this.$modal.msgSuccess('已撤回')
|
||||
this.getList()
|
||||
}).catch(() => {})
|
||||
},
|
||||
openDetail(row) {
|
||||
getApprovalDetail(row.id).then(res => {
|
||||
this.detail = res.data
|
||||
this.detailOpen = true
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
278
ruoyi-ui/src/views/oa/docs/contract/index.vue
Normal file
278
ruoyi-ui/src/views/oa/docs/contract/index.vue
Normal file
@@ -0,0 +1,278 @@
|
||||
<template>
|
||||
<div class="app-container docs-page">
|
||||
<h1>合同流 · 使用说明</h1>
|
||||
<el-alert type="info" :closable="false" show-icon
|
||||
title="合同从「谁起草」到「谁批」到「正式生效」的全过程。本页让业务员、法务、领导都清楚自己在哪一步。"
|
||||
style="margin-bottom: 20px;" />
|
||||
|
||||
<!-- ============ 泳道图 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>一、流程全景(泳道图)</b></div>
|
||||
<div class="swimlane-wrap">
|
||||
<svg viewBox="0 0 1180 560" class="swimlane-svg" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#606266" /></marker>
|
||||
<marker id="arrow-red" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#f56c6c" /></marker>
|
||||
<marker id="arrow-green" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#67c23a" /></marker>
|
||||
<marker id="arrow-dash" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#909399" /></marker>
|
||||
</defs>
|
||||
|
||||
<!-- 泳道背景 -->
|
||||
<g class="lanes">
|
||||
<rect x="120" y="20" width="1040" height="100" fill="#f9f6ff" stroke="#d3adf7" />
|
||||
<text x="20" y="76" class="lane-label">管理员</text>
|
||||
<text x="20" y="92" class="lane-sub">配置审批人</text>
|
||||
|
||||
<rect x="120" y="125" width="1040" height="120" fill="#eef9ff" stroke="#91d5ff" />
|
||||
<text x="20" y="185" class="lane-label">起草人</text>
|
||||
<text x="20" y="201" class="lane-sub">业务员 / 法务</text>
|
||||
|
||||
<rect x="120" y="250" width="1040" height="150" fill="#fff7e6" stroke="#ffd591" />
|
||||
<text x="20" y="325" class="lane-label">系统</text>
|
||||
<text x="20" y="341" class="lane-sub">自动派单 / 提醒</text>
|
||||
|
||||
<rect x="120" y="405" width="1040" height="135" fill="#f6ffed" stroke="#b7eb8f" />
|
||||
<text x="20" y="475" class="lane-label">审批人</text>
|
||||
<text x="20" y="491" class="lane-sub">领导 / 法务 / 财务</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 管理员 ===== -->
|
||||
<g class="node node-admin">
|
||||
<rect x="140" y="40" width="180" height="60" rx="6" />
|
||||
<text x="230" y="64" class="t1">① 配置「谁来批合同」</text>
|
||||
<text x="230" y="82" class="t2">选定审批人</text>
|
||||
<text x="230" y="96" class="t2">设置或签 / 会签</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 起草人 ===== -->
|
||||
<g class="node node-user">
|
||||
<rect x="140" y="150" width="180" height="70" rx="6" />
|
||||
<text x="230" y="178" class="t1">② 起草合同</text>
|
||||
<text x="230" y="196" class="t2">填合同名/甲乙方/金额</text>
|
||||
<text x="230" y="210" class="t2">/合同管理/新增</text>
|
||||
</g>
|
||||
|
||||
<g class="node node-user">
|
||||
<rect x="900" y="150" width="210" height="70" rx="6" />
|
||||
<text x="1005" y="178" class="t1">⑦ 合同生效执行</text>
|
||||
<text x="1005" y="196" class="t2">签订 / 履约 / 归档</text>
|
||||
<text x="1005" y="210" class="t2">(审批通过后才发出)</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 系统 ===== -->
|
||||
<g class="node node-sys">
|
||||
<rect x="350" y="270" width="170" height="70" rx="6" />
|
||||
<text x="435" y="296" class="t1">③ 自动生成审批单</text>
|
||||
<text x="435" y="314" class="t2">把当前审批人 +</text>
|
||||
<text x="435" y="328" class="t2">或签/会签规则定下来</text>
|
||||
</g>
|
||||
<g class="node node-sys">
|
||||
<rect x="560" y="270" width="160" height="70" rx="6" />
|
||||
<text x="640" y="296" class="t1">④ 派单给审批人</text>
|
||||
<text x="640" y="314" class="t2">在「我的审批」</text>
|
||||
<text x="640" y="328" class="t2">出现待办</text>
|
||||
</g>
|
||||
<g class="node node-sys">
|
||||
<rect x="900" y="350" width="210" height="40" rx="6" />
|
||||
<text x="1005" y="376" class="t1">⑥' 判定整单结果(或/会签)</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 审批人 ===== -->
|
||||
<g class="node node-app">
|
||||
<rect x="560" y="430" width="180" height="80" rx="6" />
|
||||
<text x="650" y="456" class="t1">⑤ 审批人处理</text>
|
||||
<text x="650" y="474" class="t2">通过 ✓ 或 驳回 ✗</text>
|
||||
<text x="650" y="490" class="t2">可写意见</text>
|
||||
<text x="650" y="506" class="t2">流水自动留痕</text>
|
||||
</g>
|
||||
|
||||
<!-- 配置 → 生成(虚线) -->
|
||||
<path d="M 320 80 C 400 80, 400 240, 435 270"
|
||||
fill="none" stroke="#909399" stroke-width="1.4" stroke-dasharray="5 4" marker-end="url(#arrow-dash)" />
|
||||
<text x="345" y="160" class="arrow-label dim">仅影响后续新单</text>
|
||||
|
||||
<!-- ② → ③ -->
|
||||
<path d="M 320 200 C 360 200, 360 250, 435 270"
|
||||
fill="none" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
|
||||
<!-- ③ → ④ -->
|
||||
<line x1="520" y1="305" x2="560" y2="305" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
|
||||
<!-- ④ → ⑤ -->
|
||||
<path d="M 640 340 C 640 380, 650 405, 650 430"
|
||||
fill="none" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
<text x="660" y="395" class="arrow-label">收到待办</text>
|
||||
|
||||
<!-- ⑤ → ⑥' -->
|
||||
<path d="M 740 460 C 850 460, 880 410, 950 390"
|
||||
fill="none" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
|
||||
<!-- ⑥' → ⑦ 通过 -->
|
||||
<path d="M 1005 350 L 1005 220"
|
||||
fill="none" stroke="#67c23a" stroke-width="2" marker-end="url(#arrow-green)" />
|
||||
<text x="1018" y="290" class="arrow-label ok">通过</text>
|
||||
|
||||
<!-- ⑥' → ② 驳回(闭环) -->
|
||||
<path d="M 900 370 C 700 370, 400 380, 230 240"
|
||||
fill="none" stroke="#f56c6c" stroke-width="2" stroke-dasharray="6 3" marker-end="url(#arrow-red)" />
|
||||
<text x="540" y="408" class="arrow-label bad">驳回 → 改完再提,自动开新单</text>
|
||||
|
||||
<!-- ⑦ → 完成 -->
|
||||
<g class="node node-end">
|
||||
<circle cx="1130" cy="185" r="22" />
|
||||
<text x="1130" y="190" class="t-end">归档</text>
|
||||
</g>
|
||||
<line x1="1110" y1="185" x2="1108" y2="185" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<span><i class="dot dot-admin"></i>管理员动作</span>
|
||||
<span><i class="dot dot-user"></i>起草人动作</span>
|
||||
<span><i class="dot dot-sys"></i>系统自动</span>
|
||||
<span><i class="dot dot-app"></i>审批人动作</span>
|
||||
<span><svg width="34" height="10"><line x1="0" y1="5" x2="30" y2="5" stroke="#67c23a" stroke-width="2"/></svg> 通过</span>
|
||||
<span><svg width="34" height="10"><line x1="0" y1="5" x2="30" y2="5" stroke="#f56c6c" stroke-width="2" stroke-dasharray="6 3"/></svg> 驳回退回</span>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 审批状态 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>二、合同列表里「审批」列的含义</b></div>
|
||||
<p class="hint">合同管理列表会显示一列「审批」,告诉你这份合同走到流程哪一步了。</p>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>「审批」列<span class="state-owner">(合同到底批没批)</span></h4>
|
||||
<div class="state-row"><el-tag size="small" type="info">未提交</el-tag><span>合同审批未启用 / 未配置,合同直接生效</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="warning">待审</el-tag><span>已派给审批人,等他们处理</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="success">通过</el-tag><span>合同可以正式签订 / 发出</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="danger">驳回</el-tag><span>修改合同内容后再保存,系统会自动新开一张审批单</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="info">撤回</el-tag><span>起草人主动撤回,本单作废</span></div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="state-card warn">
|
||||
<h4>⚠️ 卡点:未审批通过不能标记「完结」</h4>
|
||||
<p>合同的字段(甲乙方/金额/条款等)<b>不受审批拦截</b>,可以照常编辑;但要把合同状态推进到 <el-tag size="mini" type="success">完结</el-tag> 时,<b>最新审批必须是「通过」</b>,否则系统会拒绝并提示「该合同尚未审批通过」。</p>
|
||||
<p class="hint">如果整个合同审批没启用 / 未配置(列表显示"未提交"),则不会有任何拦截,完结照常推进。</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 角色操作速查 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>三、不同角色怎么用</b></div>
|
||||
<el-table :data="roleSteps" border size="small">
|
||||
<el-table-column label="你是" prop="role" width="100" />
|
||||
<el-table-column label="去哪儿" prop="menu" width="240" />
|
||||
<el-table-column label="做什么" prop="action" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 或签 vs 会签 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>四、或签 vs 会签(在合同场景下怎么选)</b></div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>或签 <el-tag size="mini" type="success">任一人即可</el-tag></h4>
|
||||
<p>审批人列表里 <b>任意一人</b> 通过 → 整张合同审批通过。</p>
|
||||
<p class="hint">适用:日常采购合同、小额合同、找最快有空的领导批即可的场景。</p>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>会签 <el-tag size="mini" type="warning">必须全员通过</el-tag></h4>
|
||||
<p>审批人列表里 <b>所有人</b> 都通过 → 整张合同审批才通过。</p>
|
||||
<p class="hint">适用:大额合同 / 跨部门合同 / 重大法务事项 — 法务 + 财务 + 主管领导都要背书。</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ FAQ ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>五、常见问题</b></div>
|
||||
<el-collapse>
|
||||
<el-collapse-item title="我新建了合同,但不想走审批,怎么办?">
|
||||
找管理员在「审批配置」里把「合同」这一行的「启用」开关关掉,新建合同就不再产生审批单,可以直接执行。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="合同已经审批通过了,但条款要改怎么办?">
|
||||
目前实现不会自动重新发起审批。如果改动比较大、有原则性变化,建议起草人在「我的发起」撤回旧单(如果还能撤回),或者重新建一份合同记录走新流程,留好痕迹。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="审批被驳回了,合同记录还在吗?">
|
||||
在。合同本身不会消失,改完字段重新保存时系统会自动新开一张审批单(流程图里那条红色虚线)。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="谁审过、什么意见去哪看?">
|
||||
起草人去「审批中心 → 我的发起」找到这张合同的审批单点「详情」,里面有完整的审批流水(谁、什么时间、通过/驳回、意见)。审批人本人在「我的审批 → 我已审批」也能看。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="我换了合同审批人,老合同的审批单上人没变?">
|
||||
单子生成时已经把审批人和或签/会签规则定下来了 —— <b>不会回溯</b>。修改只对之后新提交的合同生效。
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'OaDocContractFlow',
|
||||
data() {
|
||||
return {
|
||||
roleSteps: [
|
||||
{ role: '管理员', menu: '审批中心 → 审批配置', action: '选定合同的审批人;切换或签 / 会签;启用或停用合同审批。修改不影响已发起的旧单。' },
|
||||
{ role: '起草人', menu: '合同管理 → 新增', action: '填合同信息,保存后系统自动派单给审批人;列表的「审批」列会出现「待审」。' },
|
||||
{ role: '起草人', menu: '审批中心 → 我的发起', action: '查自己提交过的所有合同审批单 + 流水 + 意见;待审中可撤回。' },
|
||||
{ role: '审批人', menu: '审批中心 → 我的审批', action: '「待我审批」tab 里点通过 / 驳回并写意见;「我已审批」可回看历史。' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.docs-page { max-width: 1240px; }
|
||||
.docs-page h1 { font-size: 22px; margin: 0 0 16px; }
|
||||
.doc-card { margin-bottom: 16px; }
|
||||
|
||||
.swimlane-wrap { width: 100%; overflow-x: auto; }
|
||||
.swimlane-svg { width: 100%; min-width: 1100px; height: auto; display: block; }
|
||||
.swimlane-svg .lane-label { font-size: 15px; font-weight: 600; fill: #303133; }
|
||||
.swimlane-svg .lane-sub { font-size: 11px; fill: #909399; }
|
||||
.swimlane-svg .node rect { stroke-width: 1.3; }
|
||||
.swimlane-svg .node .t1 { font-size: 13px; font-weight: 600; text-anchor: middle; fill: #303133; }
|
||||
.swimlane-svg .node .t2 { font-size: 11px; text-anchor: middle; fill: #606266; }
|
||||
.swimlane-svg .node-admin rect { fill: #fff; stroke: #b37feb; }
|
||||
.swimlane-svg .node-user rect { fill: #fff; stroke: #40a9ff; }
|
||||
.swimlane-svg .node-sys rect { fill: #fff; stroke: #ffa940; }
|
||||
.swimlane-svg .node-app rect { fill: #fff; stroke: #73d13d; }
|
||||
.swimlane-svg .node-end circle { fill: #67c23a; stroke: #67c23a; }
|
||||
.swimlane-svg .node-end .t-end { font-size: 12px; fill: #fff; font-weight: 600; text-anchor: middle; }
|
||||
.swimlane-svg .arrow-label { font-size: 11px; fill: #606266; }
|
||||
.swimlane-svg .arrow-label.dim { fill: #909399; }
|
||||
.swimlane-svg .arrow-label.ok { fill: #67c23a; font-weight: 600; }
|
||||
.swimlane-svg .arrow-label.bad { fill: #f56c6c; font-weight: 600; }
|
||||
|
||||
.legend { margin-top: 12px; padding: 8px 12px; background: #fafafa; border-radius: 4px;
|
||||
display: flex; flex-wrap: wrap; gap: 18px; font-size: 12px; color: #606266; align-items: center; }
|
||||
.legend .dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 6px; vertical-align: middle; }
|
||||
.legend .dot-sys { background: #ffa940; }
|
||||
.legend .dot-user { background: #40a9ff; }
|
||||
.legend .dot-app { background: #73d13d; }
|
||||
.legend .dot-admin { background: #b37feb; }
|
||||
|
||||
.state-card { background: #fafafa; border-radius: 6px; padding: 14px 16px; height: 100%; }
|
||||
.state-card.warn { background: #fff8e6; }
|
||||
.state-card h4 { margin: 0 0 10px; font-size: 14px; }
|
||||
.state-card .state-owner { font-weight: normal; color: #909399; margin-left: 6px; font-size: 12px; }
|
||||
.state-card .state-row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: 13px; color: #606266; }
|
||||
.state-card p { line-height: 1.7; color: #303133; margin: 6px 0; }
|
||||
.state-card p.hint { color: #909399; font-size: 12px; }
|
||||
.docs-page .hint { color: #909399; font-size: 13px; margin-top: 0; }
|
||||
</style>
|
||||
295
ruoyi-ui/src/views/oa/docs/purchase/index.vue
Normal file
295
ruoyi-ui/src/views/oa/docs/purchase/index.vue
Normal file
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div class="app-container docs-page">
|
||||
<h1>采购流 · 使用说明</h1>
|
||||
<el-alert type="info" :closable="false" show-icon
|
||||
title="一张图看懂:采购需求从「谁提」到「谁审」再到「采购完成」全过程。下方的状态对照和速查表帮你弄清自己当下该做什么。"
|
||||
style="margin-bottom: 20px;" />
|
||||
|
||||
<!-- ============ 泳道图 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>一、流程全景(泳道图)</b></div>
|
||||
<div class="swimlane-wrap">
|
||||
<svg viewBox="0 0 1180 560" class="swimlane-svg" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<marker id="arrow" viewBox="0 0 10 10" refX="9" refY="5"
|
||||
markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#606266" />
|
||||
</marker>
|
||||
<marker id="arrow-red" viewBox="0 0 10 10" refX="9" refY="5"
|
||||
markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#f56c6c" />
|
||||
</marker>
|
||||
<marker id="arrow-green" viewBox="0 0 10 10" refX="9" refY="5"
|
||||
markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#67c23a" />
|
||||
</marker>
|
||||
<marker id="arrow-dash" viewBox="0 0 10 10" refX="9" refY="5"
|
||||
markerWidth="7" markerHeight="7" orient="auto-start-reverse">
|
||||
<path d="M0,0 L10,5 L0,10 z" fill="#909399" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
<!-- 泳道背景 -->
|
||||
<g class="lanes">
|
||||
<rect x="120" y="20" width="1040" height="100" fill="#f9f6ff" stroke="#d3adf7" />
|
||||
<text x="20" y="76" class="lane-label">管理员</text>
|
||||
<text x="20" y="92" class="lane-sub">配置审批人</text>
|
||||
|
||||
<rect x="120" y="125" width="1040" height="120" fill="#eef9ff" stroke="#91d5ff" />
|
||||
<text x="20" y="185" class="lane-label">发起人</text>
|
||||
<text x="20" y="201" class="lane-sub">提需求 / 推进</text>
|
||||
|
||||
<rect x="120" y="250" width="1040" height="150" fill="#fff7e6" stroke="#ffd591" />
|
||||
<text x="20" y="325" class="lane-label">系统</text>
|
||||
<text x="20" y="341" class="lane-sub">自动派单 / 提醒</text>
|
||||
|
||||
<rect x="120" y="405" width="1040" height="135" fill="#f6ffed" stroke="#b7eb8f" />
|
||||
<text x="20" y="475" class="lane-label">审批人</text>
|
||||
<text x="20" y="491" class="lane-sub">通过 / 驳回</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 管理员 ===== -->
|
||||
<g class="node node-admin">
|
||||
<rect x="140" y="40" width="180" height="60" rx="6" />
|
||||
<text x="230" y="64" class="t1">① 配置「谁来审」</text>
|
||||
<text x="230" y="82" class="t2">选定审批人</text>
|
||||
<text x="230" y="96" class="t2">设置或签 / 会签</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 发起人 ===== -->
|
||||
<g class="node node-user">
|
||||
<rect x="140" y="150" width="170" height="70" rx="6" />
|
||||
<text x="225" y="178" class="t1">② 新建采购需求</text>
|
||||
<text x="225" y="196" class="t2">填标题/物料/截止</text>
|
||||
<text x="225" y="210" class="t2">/任务/采购需求</text>
|
||||
</g>
|
||||
|
||||
<g class="node node-user">
|
||||
<rect x="900" y="150" width="210" height="70" rx="6" />
|
||||
<text x="1005" y="178" class="t1">⑦ 推进业务状态</text>
|
||||
<text x="1005" y="196" class="t2">未采购 → 采购中 → 完成</text>
|
||||
<text x="1005" y="210" class="t2">(审批通过后才放开)</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 系统 ===== -->
|
||||
<g class="node node-sys">
|
||||
<rect x="350" y="270" width="170" height="70" rx="6" />
|
||||
<text x="435" y="296" class="t1">③ 自动生成审批单</text>
|
||||
<text x="435" y="314" class="t2">把当前审批人 +</text>
|
||||
<text x="435" y="328" class="t2">或签/会签规则定下来</text>
|
||||
</g>
|
||||
<g class="node node-sys">
|
||||
<rect x="560" y="270" width="160" height="70" rx="6" />
|
||||
<text x="640" y="296" class="t1">④ 派单给审批人</text>
|
||||
<text x="640" y="314" class="t2">在「我的审批」</text>
|
||||
<text x="640" y="328" class="t2">出现待办</text>
|
||||
</g>
|
||||
<g class="node node-sys">
|
||||
<rect x="900" y="350" width="210" height="40" rx="6" />
|
||||
<text x="1005" y="376" class="t1">⑥' 判定整单结果(或/会签)</text>
|
||||
</g>
|
||||
|
||||
<!-- ===== 审批人 ===== -->
|
||||
<g class="node node-app">
|
||||
<rect x="560" y="430" width="180" height="80" rx="6" />
|
||||
<text x="650" y="456" class="t1">⑤ 审批人处理</text>
|
||||
<text x="650" y="474" class="t2">通过 ✓ 或 驳回 ✗</text>
|
||||
<text x="650" y="490" class="t2">可写意见</text>
|
||||
<text x="650" y="506" class="t2">流水自动留痕</text>
|
||||
</g>
|
||||
|
||||
<!-- ============ 箭头 ============ -->
|
||||
<!-- 配置 → 审批单生成(虚线,从管理员到 ③) -->
|
||||
<path d="M 320 80 C 400 80, 400 240, 435 270"
|
||||
fill="none" stroke="#909399" stroke-width="1.4"
|
||||
stroke-dasharray="5 4" marker-end="url(#arrow-dash)" />
|
||||
<text x="345" y="160" class="arrow-label dim">仅影响后续新单</text>
|
||||
|
||||
<!-- ② → ③ -->
|
||||
<path d="M 310 200 C 360 200, 360 250, 435 270"
|
||||
fill="none" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
|
||||
<!-- ③ → ④ -->
|
||||
<line x1="520" y1="305" x2="560" y2="305" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
|
||||
<!-- ④ → ⑤ -->
|
||||
<path d="M 640 340 C 640 380, 650 405, 650 430"
|
||||
fill="none" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
<text x="660" y="395" class="arrow-label">收到待办</text>
|
||||
|
||||
<!-- ⑤ → ⑥' 终结判定 -->
|
||||
<path d="M 740 460 C 850 460, 880 410, 950 390"
|
||||
fill="none" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
|
||||
<!-- ⑥' → ⑦ 通过 -->
|
||||
<path d="M 1005 350 L 1005 220"
|
||||
fill="none" stroke="#67c23a" stroke-width="2" marker-end="url(#arrow-green)" />
|
||||
<text x="1018" y="290" class="arrow-label ok">通过</text>
|
||||
|
||||
<!-- ⑥' → ② 驳回(闭环 / 红色) -->
|
||||
<path d="M 900 370 C 700 370, 400 380, 225 240"
|
||||
fill="none" stroke="#f56c6c" stroke-width="2" stroke-dasharray="6 3"
|
||||
marker-end="url(#arrow-red)" />
|
||||
<text x="540" y="408" class="arrow-label bad">驳回 → 改完再提,自动开新单</text>
|
||||
|
||||
<!-- ⑦ → 完成 -->
|
||||
<g class="node node-end">
|
||||
<circle cx="1130" cy="185" r="22" />
|
||||
<text x="1130" y="190" class="t-end">完成</text>
|
||||
</g>
|
||||
<line x1="1110" y1="185" x2="1108" y2="185" stroke="#606266" stroke-width="1.6" marker-end="url(#arrow)" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<span><i class="dot dot-admin"></i>管理员动作</span>
|
||||
<span><i class="dot dot-user"></i>发起人动作</span>
|
||||
<span><i class="dot dot-sys"></i>系统自动</span>
|
||||
<span><i class="dot dot-app"></i>审批人动作</span>
|
||||
<span><svg width="34" height="10"><line x1="0" y1="5" x2="30" y2="5" stroke="#67c23a" stroke-width="2"/></svg> 通过</span>
|
||||
<span><svg width="34" height="10"><line x1="0" y1="5" x2="30" y2="5" stroke="#f56c6c" stroke-width="2" stroke-dasharray="6 3"/></svg> 驳回退回</span>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 状态对照 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>二、两类状态对照(务必区分)</b></div>
|
||||
<p class="hint">列表里你会看到两列状态:左边「审批」表示这条采购需求 <b>谁来批、批了没</b>;右边「状态」表示 <b>采购到哪一步了</b>。两者独立但有关联——审批没通过,业务状态推不动。</p>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>「审批」列<span class="state-owner">(谁来批 · 批了没)</span></h4>
|
||||
<div class="state-row"><el-tag size="small" type="info">未提交</el-tag><span>该业务尚未配置审批,或审批未启用</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="warning">待审</el-tag><span>已派给审批人,等他们处理</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="success">通过</el-tag><span>审批结束,可以推进业务了</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="danger">驳回</el-tag><span>审批人不通过,改完字段再保存会自动开新单</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="info">撤回</el-tag><span>发起人自己撤回了,本单作废</span></div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>「状态」列<span class="state-owner">(采购到哪一步了)</span></h4>
|
||||
<div class="state-row"><el-tag size="small">未采购</el-tag><span>刚新建,等审批通过后才能往下推</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="warning">采购中</el-tag><span>开始下单 / 联系供应商</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="success">完成</el-tag><span>采购到货,闭环结束</span></div>
|
||||
<div class="state-row"><el-tag size="small" type="info">取消</el-tag><span>不再需要的需求,可直接取消(无需审批)</span></div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 角色操作速查 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>三、不同角色怎么用(按你是谁找对应行)</b></div>
|
||||
<el-table :data="roleSteps" border size="small">
|
||||
<el-table-column label="你是" prop="role" width="100" />
|
||||
<el-table-column label="去哪儿" prop="menu" width="240" />
|
||||
<el-table-column label="做什么 / 看什么" prop="action" />
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 或签 vs 会签 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>四、或签 vs 会签</b></div>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>或签 <el-tag size="mini" type="success">任一人即可</el-tag></h4>
|
||||
<p>审批人列表里 <b>任意一人</b> 点通过,整张单子就通过;任一人点驳回,整张单子立即驳回。</p>
|
||||
<p class="hint">适用:日常事务、找最快有空的同事盖章即可。</p>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="state-card">
|
||||
<h4>会签 <el-tag size="mini" type="warning">必须全员通过</el-tag></h4>
|
||||
<p>审批人列表里 <b>所有人</b> 都点通过,整张单子才通过;只要有一个人驳回,整张单子立即驳回。</p>
|
||||
<p class="hint">适用:跨部门、需要每个负责人都背书的事项。</p>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<!-- ============ 常见问题 ============ -->
|
||||
<el-card class="doc-card" shadow="never">
|
||||
<div slot="header"><b>五、常见问题</b></div>
|
||||
<el-collapse>
|
||||
<el-collapse-item title="状态下拉点不动,提示「尚未审批通过」?">
|
||||
采购需求必须审批通过才能进入「采购中/完成」。去「审批中心 → 我的发起」看看自己这单是不是还在「待审」或者已经被「驳回」。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="我换了审批人,为什么旧单子上的人没变?">
|
||||
单子在生成那一刻就把当时的审批人定下来了,<b>不会回溯</b>,这是为了避免审批中途偷偷换人。你的修改对后面新提交的单子生效。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="审批被驳回了,我的采购需求还在吗?">
|
||||
在。需求本身不会消失,你改完字段重新保存,系统会自动开一张新审批单。(就是流程图里那条红色虚线的回流路径。)
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="我的事项太急,能不走审批吗?">
|
||||
找管理员在「审批配置」把对应业务的「启用」临时关掉,新提交的就直接生效不走审批。事项处理完再让管理员开回来。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="我发起后又不想审批了,能撤回吗?">
|
||||
可以。在「审批中心 → 我的发起」找到这张单子,只要还是「待审」状态就能撤回;已经被审批人处理过的(通过/驳回)不能撤回。
|
||||
</el-collapse-item>
|
||||
<el-collapse-item title="为什么我在「我的审批」里看不到任何待办?">
|
||||
说明目前没人提交需要你审的单。也可能管理员没把你配进审批人列表里,去问下管理员。
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'OaDocPurchaseFlow',
|
||||
data() {
|
||||
return {
|
||||
roleSteps: [
|
||||
{ role: '管理员', menu: '审批中心 → 审批配置', action: '选定每个业务的审批人;切换或签 / 会签;启用或停用某个业务的审批。修改不影响已经发起的旧单。' },
|
||||
{ role: '发起人', menu: '任务 → 采购需求', action: '点「新增」填写需求,保存后系统自动派给审批人;列表里会出现「待审」徽标。' },
|
||||
{ role: '发起人', menu: '审批中心 → 我的发起', action: '看自己提交过的所有审批单 + 完整流水 + 审批意见;待审中可撤回。' },
|
||||
{ role: '审批人', menu: '审批中心 → 我的审批', action: '「待我审批」tab 里点通过 / 驳回并写意见;「我已审批」可回看自己处理过的历史。' }
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.docs-page { max-width: 1240px; }
|
||||
.docs-page h1 { font-size: 22px; margin: 0 0 16px; }
|
||||
.doc-card { margin-bottom: 16px; }
|
||||
|
||||
.swimlane-wrap { width: 100%; overflow-x: auto; }
|
||||
.swimlane-svg { width: 100%; min-width: 1100px; height: auto; display: block; }
|
||||
.swimlane-svg .lane-label { font-size: 15px; font-weight: 600; fill: #303133; }
|
||||
.swimlane-svg .lane-sub { font-size: 11px; fill: #909399; }
|
||||
.swimlane-svg .node rect { stroke-width: 1.3; }
|
||||
.swimlane-svg .node .t1 { font-size: 13px; font-weight: 600; text-anchor: middle; fill: #303133; }
|
||||
.swimlane-svg .node .t2 { font-size: 11px; text-anchor: middle; fill: #606266; }
|
||||
.swimlane-svg .node-admin rect { fill: #fff; stroke: #b37feb; }
|
||||
.swimlane-svg .node-user rect { fill: #fff; stroke: #40a9ff; }
|
||||
.swimlane-svg .node-sys rect { fill: #fff; stroke: #ffa940; }
|
||||
.swimlane-svg .node-app rect { fill: #fff; stroke: #73d13d; }
|
||||
.swimlane-svg .node-end circle { fill: #67c23a; stroke: #67c23a; }
|
||||
.swimlane-svg .node-end .t-end { font-size: 12px; fill: #fff; font-weight: 600; text-anchor: middle; }
|
||||
.swimlane-svg .arrow-label { font-size: 11px; fill: #606266; }
|
||||
.swimlane-svg .arrow-label.dim { fill: #909399; }
|
||||
.swimlane-svg .arrow-label.ok { fill: #67c23a; font-weight: 600; }
|
||||
.swimlane-svg .arrow-label.bad { fill: #f56c6c; font-weight: 600; }
|
||||
|
||||
.legend {
|
||||
margin-top: 12px; padding: 8px 12px; background: #fafafa; border-radius: 4px;
|
||||
display: flex; flex-wrap: wrap; gap: 18px; font-size: 12px; color: #606266; align-items: center;
|
||||
}
|
||||
.legend .dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; margin-right: 6px; vertical-align: middle; }
|
||||
.legend .dot-sys { background: #ffa940; }
|
||||
.legend .dot-user { background: #40a9ff; }
|
||||
.legend .dot-app { background: #73d13d; }
|
||||
.legend .dot-admin { background: #b37feb; }
|
||||
|
||||
.state-card { background: #fafafa; border-radius: 6px; padding: 14px 16px; height: 100%; }
|
||||
.state-card h4 { margin: 0 0 10px; font-size: 14px; }
|
||||
.state-card .state-owner { font-weight: normal; color: #909399; margin-left: 6px; font-size: 12px; }
|
||||
.state-card .state-row { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; font-size: 13px; color: #606266; }
|
||||
.state-card p { line-height: 1.7; color: #303133; margin: 6px 0; }
|
||||
.state-card p.hint { color: #909399; font-size: 12px; }
|
||||
.docs-page .hint { color: #909399; font-size: 13px; margin-top: 0; }
|
||||
</style>
|
||||
@@ -93,6 +93,15 @@
|
||||
<span>{{ parseTime(scope.row.signTime, '{y}-{m}-{d}') }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="审批" align="center" prop="approvalStatus" width="90">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.approvalStatus == null" size="mini" type="info">未提交</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 0" size="mini" type="warning">待审</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 1" size="mini" type="success">通过</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 2" size="mini" type="danger">驳回</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 3" size="mini" type="info">撤回</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="contractStatus" width="120">
|
||||
<template slot-scope="scope">
|
||||
<dict-tag :options="dict.type.sys_show_hide" :value="scope.row.contractStatus"/>
|
||||
|
||||
@@ -114,6 +114,15 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<!-- 表格中的状态列(替换原有列) -->
|
||||
<el-table-column label="审批" align="center" prop="approvalStatus" width="90">
|
||||
<template slot-scope="{ row }">
|
||||
<el-tag v-if="row.approvalStatus == null" size="mini" type="info">未提交</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 0" size="mini" type="warning">待审</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 1" size="mini" type="success">通过</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 2" size="mini" type="danger">驳回</el-tag>
|
||||
<el-tag v-else-if="row.approvalStatus === 3" size="mini" type="info">撤回</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="状态" align="center" prop="status" width="160">
|
||||
<template slot-scope="{ row }">
|
||||
<el-select v-model="row.status" size="mini" placeholder="选择状态"
|
||||
|
||||
Reference in New Issue
Block a user