Browse Source

feat(home): 添加供应商文档到期提醒功能

- 新增 home.vue 页面实现代办清单展示
- 集成供应商文档上传到期提醒功能
- 实现合同续签请求提醒功能
- 添加 API 接口支持文档和合同提醒查询
- 创建供应商合同续签请求管理页面
- 实现点击卡片跳转到相应供应商列表功能
- 添加悬停动画效果和视觉反馈机制
master
qiankanghui 2 months ago
parent
commit
f06a87168c
  1. 2
      src/api/srm/srmSupplier.js
  2. 395
      src/views/common/home.vue
  3. 32
      src/views/modules/srmSupplier/supplierContractRequest.vue
  4. 50
      src/views/modules/srmSupplier/supplierList.vue

2
src/api/srm/srmSupplier.js

@ -2,6 +2,8 @@ import { createAPI } from '@/utils/httpRequest.js'
export const searchSrmSupplierList = data => createAPI('/srmSupplier/searchSrmSupplierList', 'POST', data)
export const supplierDocExpireReminder = data => createAPI('/srmSupplier/supplierDocExpireReminder', 'POST', data || {})
export const supplierContractRequestExpireReminder = data => createAPI('/srmSupplier/supplierContractRequestExpireReminder', 'POST', data || {})
export const searchSupplierGroupRequestList = data => createAPI('/srmSupplier/searchSupplierGroupRequestList', 'POST', data)
export const getSupplierGroupRequestList = data => createAPI('/srmSupplier/getSupplierGroupRequestList', 'POST', data)
export const createNewSupplierRequest = data => createAPI('/srmSupplier/createNewSupplierRequest', 'POST', data)

395
src/views/common/home.vue

@ -1,70 +1,363 @@
<template>
<div class="mod-home">
<div v-if="appName==='CKP'" style="width: 100%;height: 100%">
<iframe frameborder="0" style="width: 100%;height: 100%" :src="url"> </iframe>
</div>
<div v-else>
<h3>旭捷</h3>
<ul>
<li><h3>欢迎: {{ userName }}!!!</h3></li>
</ul>
</div>
<div
class="mod-home mod-home--tech"
:class="{ 'mod-home--pulse-off': !homeCardPulseEnabled }">
<div class="tech-backdrop">
<div class="tech-frame">
<header class="tech-header tech-header--simple">
<h1 class="tech-header__title">代办清单</h1>
</header>
<div class="home-reminders">
<div
v-show="homeTileShow.supplierDocExpire"
class="home-reminder-card tech-card"
:class="{ 'is-clickable': docExpireReminderCount > 0, 'tech-card--active': docExpireReminderCount > 0 }"
@click="onDocCardClick">
<div class="tech-card__grid" aria-hidden="true"></div>
<div class="home-reminder-card__title">Supplier Document Upload</div>
<div class="home-reminder-card__count">{{ docExpireReminderCount }}</div>
<div class="home-reminder-card__hint">{{ docExpireReminderCount > 0 ? 'Jump to supplier list' : 'Null' }}</div>
</div>
<div
v-show="homeTileShow.contractRequestExpire"
class="home-reminder-card tech-card"
:class="{ 'is-clickable': contractExpireReminderCount > 0, 'tech-card--active': contractExpireReminderCount > 0 }"
@click="onContractCardClick">
<div class="tech-card__grid" aria-hidden="true"></div>
<div class="home-reminder-card__title">Supplier Contract Renew Request</div>
<div class="home-reminder-card__count">{{ contractExpireReminderCount }}</div>
<div class="home-reminder-card__hint">{{ contractExpireReminderCount > 0 ? 'Jump to Renew Request' : 'Null' }}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import {searchSysLanguagePack} from "../../api/sysLanguage";
import {searchSysLanguagePack} from "../../api/sysLanguage";
import { supplierDocExpireReminder, supplierContractRequestExpireReminder } from '@/api/srm/srmSupplier.js'
export default {
data(){
return {
// url:"http://xujiesoft.vicp.net:9191/bi/Show?uid=be15368c-bceb-11ef-9785-000c29f2c2b8",
//url:"http://192.168.1.90:9191/chart/reportShow?uid=3c800188-b04b-11ef-abf0-000c2947c1cd",
url:"http://172.26.68.20:9191/bi/Show?uid=eefa66f1-b058-11ef-a145-3868dd5b136a",
appName:this.$store.state.common.appName,
}
},
methods: {
languageRefresh(){
console.log("欢迎使用 旭捷管理系统!!!")
export default {
data(){
return {
appName:this.$store.state.common.appName,
docExpireReminderCount: 0,
docExpireReminderIds: [],
contractExpireReminderCount: 0,
contractExpireReminderIds: [],
homeTileShow: {
supplierDocExpire: true,
contractRequestExpire: true,
},
languagePack() {
// searchSysLanguagePack().then(({data}) => {
// this.$i18n.mergeLocaleMessage('en', data.data.en)
// this.$i18n.mergeLocaleMessage('cn', data.data.cn)
// })
}
/** 代办卡片悬停外圈脉冲(双环);false 时无任何脉冲描边 */
homeCardPulseEnabled: false,
}
},
methods: {
onDocCardClick () {
this.goSupplierDocExpireList()
},
onContractCardClick () {
this.goContractRequestExpireList()
},
computed: {
userName: {
get() {
return this.$store.state.user.userDisplay
loadDocExpireReminder () {
const site = this.$store.state.user.site
supplierDocExpireReminder({ site }).then(({ data }) => {
if (data && data.code === 0 && data.data) {
this.docExpireReminderCount = data.data.count != null ? data.data.count : 0
this.docExpireReminderIds = Array.isArray(data.data.supplierIds) ? data.data.supplierIds : []
}
},
mainTabs: {
get() {
return this.$store.state.common.mainTabs
},
set(val) {
this.$store.commit('common/updateMainTabs', val)
}).catch(() => {})
},
goSupplierDocExpireList () {
if (!this.docExpireReminderCount) return
const ids = this.docExpireReminderIds.join(',')
if (!ids) return
this.$router.push({ name: 'srmSupplier-supplierList', query: { docExpireSupplierIds: ids } })
},
loadContractExpireReminder () {
const site = this.$store.state.user.site
supplierContractRequestExpireReminder({ site }).then(({ data }) => {
if (data && data.code === 0 && data.data) {
this.contractExpireReminderCount = data.data.count != null ? data.data.count : 0
this.contractExpireReminderIds = Array.isArray(data.data.contractRequestIds) ? data.data.contractRequestIds : []
}
}).catch(() => {})
},
goContractRequestExpireList () {
if (!this.contractExpireReminderCount) return
const ids = this.contractExpireReminderIds.join(',')
if (!ids) return
this.$router.push({ name: 'srmSupplier-supplierContractRequest', query: { contractExpireRequestIds: ids } })
},
languageRefresh(){
console.log("欢迎使用 旭捷管理系统!!!")
},
languagePack() {
// searchSysLanguagePack().then(({data}) => {
// this.$i18n.mergeLocaleMessage('en', data.data.en)
// this.$i18n.mergeLocaleMessage('cn', data.data.cn)
// })
}
},
computed: {
userName: {
get() {
return this.$store.state.user.userDisplay
}
},
beforeMount() {
this.languageRefresh()
this.languagePack()
mainTabs: {
get() {
return this.$store.state.common.mainTabs
},
set(val) {
this.$store.commit('common/updateMainTabs', val)
}
}
},
beforeMount() {
this.languageRefresh()
this.languagePack()
},
mounted () {
if (this.homeTileShow.supplierDocExpire) {
this.loadDocExpireReminder()
}
if (this.homeTileShow.contractRequestExpire) {
this.loadContractExpireReminder()
}
},
watch: {
'homeTileShow.supplierDocExpire'(v) {
if (v) {
this.loadDocExpireReminder()
}
},
'homeTileShow.contractRequestExpire'(v) {
if (v) {
this.loadContractExpireReminder()
}
}
},
}
}
</script>
<style>
.mod-home {
line-height: 1.5;
height: 91vh;
<style scoped>
/* ---------- 浅色简约首页 ---------- */
.mod-home--tech {
--tech-border-hover: #38bdf8;
--home-count-color: #ff0000;
--home-count-muted: #94a3b8;
--card-pulse-opacity-peak: 0.92;
--card-pulse-border: rgba(14, 165, 233, 0.88);
--card-pulse-border-mid: rgba(14, 165, 233, 0.72);
--card-pulse-glow: 0 0 20px rgba(56, 189, 248, 0.48);
line-height: 1.5;
height: 91vh;
width: 100%;
box-sizing: border-box;
position: relative;
overflow: hidden;
font-family: 'Segoe UI', 'Microsoft YaHei', 'PingFang SC', system-ui, sans-serif;
color: #475569;
}
.mod-home--tech.mod-home--pulse-off .tech-card::before,
.mod-home--tech.mod-home--pulse-off .tech-card::after {
display: none;
}
.tech-backdrop {
min-height: 100%;
position: relative;
padding: 28px 22px 36px;
box-sizing: border-box;
background: #fff;
}
.tech-frame {
position: relative;
z-index: 3;
max-width: 1180px;
margin: 0 auto;
padding: 0;
border-radius: 0;
background: transparent;
box-shadow: none;
}
.tech-header--simple {
margin: 0 0 22px 0;
padding: 0 0 12px 0;
text-align: left;
position: relative;
}
.tech-header--simple::after {
content: '';
position: absolute;
left: 0;
bottom: 0;
width: 120px;
height: 2px;
border-radius: 2px;
background: linear-gradient(90deg, #0ea5e9, #6366f1, transparent);
opacity: 0.88;
box-shadow: 0 0 14px rgba(14, 165, 233, 0.35);
}
.tech-header__title {
margin: 0;
font-size: 20px;
font-weight: 700;
letter-spacing: 0.2em;
line-height: 1.35;
color: #334155;
}
.home-reminders {
padding: 12px 10px 8px;
display: flex;
flex-wrap: wrap;
gap: 22px;
align-items: stretch;
justify-content: flex-start;
}
.home-reminder-card.tech-card {
width: calc(100% / 6);
min-width: 168px;
box-sizing: border-box;
padding: 22px 16px 20px;
text-align: center;
position: relative;
overflow: visible;
border-radius: 12px;
border: 1px solid #e2e8f0;
background: linear-gradient(165deg, #ffffff 0%, #fafbfc 52%, #f8fafc 100%);
box-shadow:
0 1px 2px rgba(15, 23, 42, 0.04),
0 4px 16px rgba(15, 23, 42, 0.06);
color: #334155;
transition:
transform 0.22s ease,
box-shadow 0.22s ease,
border-color 0.28s ease;
}
.tech-card::before,
.tech-card::after {
content: '';
position: absolute;
left: 50%;
top: 50%;
width: 100%;
height: 100%;
border-radius: inherit;
border: 2px solid var(--card-pulse-border-mid);
opacity: 0;
pointer-events: none;
z-index: 1;
transform: translate(-50%, -50%) scale(1);
box-sizing: border-box;
box-shadow: var(--card-pulse-glow);
}
.tech-card:hover::before {
animation: card-hover-ripple 1.45s ease-out infinite;
}
.tech-card:hover::after {
animation: card-hover-ripple 1.45s ease-out 0.48s infinite;
}
@keyframes card-hover-ripple {
0% {
transform: translate(-50%, -50%) scale(0.88);
opacity: var(--card-pulse-opacity-peak);
border-color: var(--card-pulse-border);
}
</style>
100% {
transform: translate(-50%, -50%) scale(1.28);
opacity: 0;
border-color: rgba(56, 189, 248, 0);
}
}
.tech-card__grid {
position: absolute;
inset: 0;
z-index: 0;
border-radius: inherit;
opacity: 0.35;
background-image:
linear-gradient(rgba(148, 163, 184, 0.12) 1px, transparent 1px),
linear-gradient(90deg, rgba(148, 163, 184, 0.08) 1px, transparent 1px);
background-size: 18px 18px;
pointer-events: none;
}
.tech-card--active {
border-color: rgba(14, 165, 233, 0.42);
box-shadow:
0 0 0 1px rgba(56, 189, 248, 0.12),
0 6px 20px rgba(15, 23, 42, 0.08),
0 0 20px rgba(14, 165, 233, 0.08);
}
.home-reminder-card.is-clickable {
cursor: pointer;
}
.tech-card.is-clickable:hover {
transform: translateY(-4px);
border-color: var(--tech-border-hover);
box-shadow:
0 0 0 1px rgba(56, 189, 248, 0.2),
0 8px 28px rgba(15, 23, 42, 0.08),
0 0 32px rgba(56, 189, 248, 0.12);
}
.home-reminder-card__title {
position: relative;
z-index: 3;
font-size: 14px;
font-weight: 600;
color: #334155;
line-height: 1.45;
}
.home-reminder-card__count {
position: relative;
z-index: 3;
font-size: 36px;
font-weight: 800;
font-variant-numeric: tabular-nums;
margin-top: 14px;
line-height: 1;
color: var(--home-count-color);
}
.home-reminder-card:not(.tech-card--active) .home-reminder-card__count {
color: var(--home-count-muted);
}
.home-reminder-card__hint {
position: relative;
z-index: 3;
font-size: 11px;
margin-top: 12px;
color: #64748b;
letter-spacing: 0.06em;
opacity: 0.95;
}
.home-reminder-card__meta {
font-size: 12px;
color: #64748b;
margin-top: 6px;
}
</style>

32
src/views/modules/srmSupplier/supplierContractRequest.vue

@ -466,13 +466,35 @@ export default {
this.requestModelData.supplierNo = this.requestModelData.supplierNo.toUpperCase()
}
},
'$route.query.contractExpireRequestIds'(val, oldVal) {
if (val === oldVal) return
if (this._suppressContractExpireRouteWatch) {
this._suppressContractExpireRouteWatch = false
return
}
this.loadMainOrReminder()
}
},
mounted() {
this.$nextTick(() => {
this.height = window.innerHeight - 220;
this.loadMainOrReminder()
})
},
methods: {
loadMainOrReminder () {
const raw = this.$route.query.contractExpireRequestIds
if (raw !== undefined && raw !== null && String(raw).trim() !== '') {
const ids = String(raw).split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n))
if (ids.length) {
this.$set(this.searchData, 'contractRequestIdList', ids)
this.pageIndex = 1
this.getMainData({ clearReminderIdFilterAfter: true })
return
}
}
this.getMainData()
},
// S
getBaseList(val, type) {
this.tagNo = val
@ -505,7 +527,8 @@ export default {
}
}
},
getMainData(){
getMainData (opts) {
const clearReminderIdFilterAfter = opts && opts.clearReminderIdFilterAfter === true
this.searchData.limit = this.pageSize
this.searchData.page = this.pageIndex
getContractRequestList(this.searchData).then(({data}) => {
@ -514,6 +537,13 @@ export default {
this.pageIndex = data.page.currPage
this.pageSize = data.page.pageSize
this.totalPage = data.page.totalCount
if (clearReminderIdFilterAfter) {
this.$delete(this.searchData, 'contractRequestIdList')
const q = Object.assign({}, this.$route.query)
delete q.contractExpireRequestIds
this._suppressContractExpireRouteWatch = true
this.$router.replace({ name: this.$route.name, query: q, params: this.$route.params })
}
}
this.dataListLoading = false
})

50
src/views/modules/srmSupplier/supplierList.vue

@ -1099,9 +1099,33 @@ export default {
mounted () {
this.$nextTick(() => {
this.height = (window.innerHeight - 220) / 2
this.loadMainOrReminder()
})
},
watch: {
'$route.query.docExpireSupplierIds'(val, oldVal) {
if (val === oldVal) return
if (this._suppressDocExpireRouteWatch) {
this._suppressDocExpireRouteWatch = false
return
}
this.loadMainOrReminder()
}
},
methods: {
loadMainOrReminder () {
const raw = this.$route.query.docExpireSupplierIds
if (raw !== undefined && raw !== null && String(raw).trim() !== '') {
const ids = String(raw).split(',').map(s => parseInt(s.trim(), 10)).filter(n => !isNaN(n))
if (ids.length) {
this.$set(this.searchData, 'supplierIdList', ids)
this.pageIndex = 1
this.getMainData({ clearReminderIdFilterAfter: true })
return
}
}
this.getMainData()
},
getBaseList (val, type) {
this.tagNo = val
this.$nextTick(() => {
@ -1156,7 +1180,8 @@ export default {
this.currentSupplier.deliveryTerm = val.DeliveryTermID
}
},
getMainData () {
getMainData (opts) {
const clearReminderIdFilterAfter = opts && opts.clearReminderIdFilterAfter === true
this.searchData.limit = this.pageSize
this.searchData.page = this.pageIndex
searchSrmSupplierList(this.searchData).then(({data}) => {
@ -1165,14 +1190,31 @@ export default {
this.pageIndex = data.page.currPage
this.pageSize = data.page.pageSize
this.totalPage = data.page.totalCount
//
this.$nextTick(() => {
if (this.$refs.mainTable) {
this.$refs.mainTable.clearSelection()
}
this.mainTableSelections = []
})
if (clearReminderIdFilterAfter) {
this.activeName = 'documents'
}
//
if (this.mainDataList.length > 0) {
//
//
this.$refs.mainTable.setCurrentRow(this.mainDataList[0])
this.changeData(this.mainDataList[0])
} else {
}else {
this.changeData(null)
}
if (clearReminderIdFilterAfter) {
this.$delete(this.searchData, 'supplierIdList')
const q = Object.assign({}, this.$route.query)
delete q.docExpireSupplierIds
this._suppressDocExpireRouteWatch = true
this.$router.replace({ name: this.$route.name, query: q, params: this.$route.params })
}
}
this.dataListLoading = false
})

Loading…
Cancel
Save