提交
No files matched your search
@@ -6,7 +6,7 @@
|
||||
<view class="up-title">添加附件</view>
|
||||
<view class="up-sub">支持 PDF / 图片 / Office,可多选上传</view>
|
||||
</view>
|
||||
<van-icon name="arrow" color="#16a34a" />
|
||||
<van-icon name="arrow" color="#22c55e" />
|
||||
</view>
|
||||
|
||||
<view
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
<van-button size="small" plain hairline type="default" @click.stop="$emit('view', item)">
|
||||
查看
|
||||
</van-button>
|
||||
<van-button size="small" type="primary" color="#16a34a" @click.stop="$emit('action', item)">
|
||||
<van-button size="small" type="primary" color="#22c55e" @click.stop="$emit('action', item)">
|
||||
处理
|
||||
</van-button>
|
||||
</view>
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
class="menu-card"
|
||||
@click="$emit('select', m)"
|
||||
>
|
||||
<view class="menu-icon" :class="toneClass(m)" :style="iconBoxStyle(m)">
|
||||
<VanIcon :name="m.icon" :color="iconFg(m)" size="52rpx" />
|
||||
<view
|
||||
class="menu-icon"
|
||||
:class="[toneClass(m), plainClass(m)]"
|
||||
:style="iconBoxStyle(m)"
|
||||
>
|
||||
<VanIcon :name="m.icon" :color="iconFg(m)" :size="iconSize(m)" />
|
||||
</view>
|
||||
<text class="menu-title">{{ m.title }}</text>
|
||||
</view>
|
||||
@@ -32,10 +36,21 @@
|
||||
iconFg(m) {
|
||||
return this.colors(m).iconFg || ''
|
||||
},
|
||||
isPlainIcon(m) {
|
||||
const bg = String(this.colors(m).iconBg || '').trim().toLowerCase()
|
||||
return bg === 'transparent' || bg === 'none'
|
||||
},
|
||||
plainClass(m) {
|
||||
return this.isPlainIcon(m) ? 'menu-icon--plain' : ''
|
||||
},
|
||||
iconSize(m) {
|
||||
return this.isPlainIcon(m) ? '56rpx' : '52rpx'
|
||||
},
|
||||
iconBoxStyle(m) {
|
||||
const { iconBg, iconFg } = this.colors(m)
|
||||
const style = {}
|
||||
if (iconBg) style.background = iconBg
|
||||
if (this.isPlainIcon(m)) style.boxShadow = 'none'
|
||||
if (iconFg) style.color = iconFg
|
||||
return style
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@ export default {
|
||||
name: 'OaAppIcon',
|
||||
props: {
|
||||
iconUrl: { type: String, default: '' },
|
||||
/** sm=32, card=36, md=56 */
|
||||
/** xs=28, sm=32, card=36, md=40, lg=48 */
|
||||
size: { type: String, default: 'sm' }
|
||||
},
|
||||
computed: {
|
||||
@@ -51,35 +51,50 @@ export default {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.oa-app-icon--xs {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 7px;
|
||||
}
|
||||
.oa-app-icon--sm {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 8px;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
.oa-app-icon--card {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 8px;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
.oa-app-icon--md {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
.oa-app-icon--lg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
background: #f1f5f9;
|
||||
}
|
||||
.oa-app-icon__glyph {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.oa-app-icon--xs .oa-app-icon__glyph {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
.oa-app-icon--card .oa-app-icon__glyph {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
.oa-app-icon--md .oa-app-icon__glyph {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
.oa-app-icon--lg .oa-app-icon__glyph {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.oa-app-icon__photo {
|
||||
width: 100%;
|
||||
|
||||
@@ -35,6 +35,8 @@
|
||||
'eye-o': '◎',
|
||||
down: '↓',
|
||||
checked: '✓',
|
||||
search: '⌕',
|
||||
clear: '×',
|
||||
'manager-o': '☺',
|
||||
'clock-o': '◷',
|
||||
description: '📄',
|
||||
@@ -47,7 +49,12 @@
|
||||
|
||||
/** 本地位图(小程序 <image> 真机可靠);颜色已烘焙在 PNG 内 */
|
||||
const PNG_ICONS = {
|
||||
approve: '/static/menu-icons/start-approval.png'
|
||||
approve: '/static/menu-icons/start-approval.png',
|
||||
'oa-drafts': '/static/menu-icons/oa-drafts.png',
|
||||
'oa-pending': '/static/menu-icons/oa-pending.png',
|
||||
'oa-done': '/static/menu-icons/oa-done.png',
|
||||
'oa-mine': '/static/menu-icons/oa-mine.png',
|
||||
'oa-cc': '/static/menu-icons/oa-cc.png'
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
@@ -57,42 +57,42 @@
|
||||
}
|
||||
|
||||
.v-tag.is-primary {
|
||||
background: #f0fdf4;
|
||||
color: #15803d;
|
||||
background: #ecfdf5;
|
||||
color: #16a34a;
|
||||
}
|
||||
|
||||
.v-tag.is-success {
|
||||
background: #f0fdf4;
|
||||
color: #189b65;
|
||||
background: #ecfdf5;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.v-tag.is-warning {
|
||||
background: #fff7e8;
|
||||
color: #cf8a19;
|
||||
background: #fffbeb;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.v-tag.is-danger {
|
||||
background: #fdecec;
|
||||
color: #d14343;
|
||||
background: #fef2f2;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.v-tag.is-plain.is-primary,
|
||||
.v-tag.is-plain.is-success {
|
||||
background: transparent;
|
||||
border-color: #16a34a;
|
||||
color: #16a34a;
|
||||
border-color: #22c55e;
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.v-tag.is-plain.is-warning {
|
||||
background: transparent;
|
||||
border-color: #cf8a19;
|
||||
color: #cf8a19;
|
||||
border-color: #d97706;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.v-tag.is-plain.is-danger {
|
||||
background: transparent;
|
||||
border-color: #d14343;
|
||||
color: #d14343;
|
||||
border-color: #ef4444;
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.v-tag.is-plain.is-default {
|
||||
|
||||
@@ -36,24 +36,24 @@
|
||||
"path": "pages/oa/form-sink/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "表单水槽测试",
|
||||
"navigationBarBackgroundColor": "#16a34a",
|
||||
"navigationBarTextStyle": "white"
|
||||
"navigationBarBackgroundColor": "#ecfdf5",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/oa/start/index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发起审批",
|
||||
"navigationBarBackgroundColor": "#16a34a",
|
||||
"navigationBarTextStyle": "white"
|
||||
"navigationBarBackgroundColor": "#ecfdf5",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/oa/start/webview",
|
||||
"style": {
|
||||
"navigationBarTitleText": "填写审批",
|
||||
"navigationBarBackgroundColor": "#16a34a",
|
||||
"navigationBarTextStyle": "white"
|
||||
"navigationBarBackgroundColor": "#ecfdf5",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -86,10 +86,10 @@
|
||||
}
|
||||
],
|
||||
"globalStyle": {
|
||||
"navigationBarTextStyle": "white",
|
||||
"navigationBarTextStyle": "black",
|
||||
"navigationBarTitleText": "中科兴皖",
|
||||
"navigationBarBackgroundColor": "#16a34a",
|
||||
"backgroundColor": "#f5f8f7"
|
||||
"navigationBarBackgroundColor": "#ecfdf5",
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"uniIdRouter": {}
|
||||
}
|
||||
@@ -13,14 +13,14 @@
|
||||
<view class="panel">
|
||||
<view class="panel-hd"><text class="panel-title">按钮 Button</text></view>
|
||||
<view class="comp-row">
|
||||
<van-button type="primary" size="small" color="#16a34a" round>主按钮</van-button>
|
||||
<van-button type="primary" size="small" plain hairline color="#16a34a" round>描边</van-button>
|
||||
<van-button type="primary" size="small" color="#22c55e" round>主按钮</van-button>
|
||||
<van-button type="primary" size="small" plain hairline color="#22c55e" round>描边</van-button>
|
||||
<van-button size="small" plain hairline round>次要</van-button>
|
||||
<van-button type="danger" size="small" plain hairline round>危险</van-button>
|
||||
</view>
|
||||
<van-button
|
||||
type="primary"
|
||||
color="#16a34a"
|
||||
color="#22c55e"
|
||||
block
|
||||
round
|
||||
style="margin-top: 16rpx; height: 88rpx;"
|
||||
@@ -33,7 +33,7 @@
|
||||
<view class="panel-hd"><text class="panel-title">状态 Tag</text></view>
|
||||
<view class="comp-row">
|
||||
<van-tag type="warning" plain round>待处理</van-tag>
|
||||
<van-tag type="primary" color="#16a34a" round>进行中</van-tag>
|
||||
<van-tag type="primary" color="#22c55e" round>进行中</van-tag>
|
||||
<van-tag type="success" plain round>已完成</van-tag>
|
||||
<van-tag type="danger" plain round>异常</van-tag>
|
||||
</view>
|
||||
@@ -45,7 +45,7 @@
|
||||
left-icon="volume-o"
|
||||
text="移动端菜单在「菜单管理 → 移动端菜单」下配置,按角色授权后展示。"
|
||||
background="#f0fdf4"
|
||||
color="#15803d"
|
||||
color="#16a34a"
|
||||
/>
|
||||
<view style="min-height: 360rpx; margin-top: 24rpx">
|
||||
<van-empty
|
||||
@@ -54,17 +54,17 @@
|
||||
/>
|
||||
</view>
|
||||
<view style="display: flex; justify-content: center; padding: 32rpx 0">
|
||||
<van-loading size="24px" color="#16a34a">加载中</van-loading>
|
||||
<van-loading size="24px" color="#22c55e">加载中</van-loading>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="panel">
|
||||
<view class="panel-hd"><text class="panel-title">反馈</text></view>
|
||||
<view class="comp-row">
|
||||
<van-button size="small" plain hairline color="#16a34a" @click="toast('操作成功')">
|
||||
<van-button size="small" plain hairline color="#22c55e" @click="toast('操作成功')">
|
||||
Toast
|
||||
</van-button>
|
||||
<van-button size="small" plain hairline color="#16a34a" @click="showDialog">
|
||||
<van-button size="small" plain hairline color="#22c55e" @click="showDialog">
|
||||
Dialog
|
||||
</van-button>
|
||||
</view>
|
||||
@@ -99,7 +99,7 @@
|
||||
uni.showModal({
|
||||
title: '确认提交',
|
||||
content: '提交后将进入审批流程,是否继续?',
|
||||
confirmColor: '#16a34a'
|
||||
confirmColor: '#22c55e'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
|
||||
<view class="detail-section">
|
||||
<view class="sec-hd"><text class="sec-title">业务进度</text></view>
|
||||
<van-steps :active="detail.stepActive" active-color="#16a34a">
|
||||
<van-steps :active="detail.stepActive" active-color="#22c55e">
|
||||
<van-step v-for="s in detail.steps" :key="s">{{ s }}</van-step>
|
||||
</van-steps>
|
||||
</view>
|
||||
@@ -94,7 +94,7 @@
|
||||
|
||||
<view class="detail-footer">
|
||||
<van-button class="btn-flex" plain hairline type="default" @click="goBack">返回</van-button>
|
||||
<van-button class="btn-flex" type="primary" color="#16a34a" round @click="goEdit">
|
||||
<van-button class="btn-flex" type="primary" color="#22c55e" round @click="goEdit">
|
||||
编辑处理
|
||||
</van-button>
|
||||
</view>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
/>
|
||||
<van-cell title="加急">
|
||||
<template #right-icon>
|
||||
<van-switch v-model="form.urgent" size="20px" active-color="#16a34a" />
|
||||
<van-switch v-model="form.urgent" size="20px" active-color="#22c55e" />
|
||||
</template>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
@@ -62,7 +62,7 @@
|
||||
|
||||
<view class="form-footer">
|
||||
<van-button class="btn-flex" plain hairline type="default" @click="goBack">取消</van-button>
|
||||
<van-button class="btn-flex" type="primary" color="#16a34a" round @click="onSave">
|
||||
<van-button class="btn-flex" type="primary" color="#22c55e" round @click="onSave">
|
||||
保存
|
||||
</van-button>
|
||||
</view>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
shape="round"
|
||||
background="transparent"
|
||||
placeholder="搜索单号 / 名称 / 负责人"
|
||||
@update:model-value="onKeywordInput"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
@@ -79,6 +80,9 @@
|
||||
},
|
||||
methods: {
|
||||
tabCount,
|
||||
onKeywordInput(v) {
|
||||
this.keyword = v == null ? '' : String(v)
|
||||
},
|
||||
goHome() {
|
||||
uni.reLaunch({ url: '/pages/index/index' })
|
||||
},
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
:circular="banners.length > 1"
|
||||
:indicator-dots="banners.length > 1"
|
||||
indicator-color="rgba(255,255,255,0.45)"
|
||||
indicator-active-color="#16a34a"
|
||||
indicator-active-color="#22c55e"
|
||||
:autoplay="false"
|
||||
>
|
||||
<swiper-item v-for="item in banners" :key="item.id">
|
||||
@@ -86,8 +86,7 @@
|
||||
title: '发起审批',
|
||||
icon: 'approve',
|
||||
path: '/pages/oa/start/index',
|
||||
iconBg: '#ffe4e6',
|
||||
iconFg: '#e11d48'
|
||||
iconBg: 'transparent'
|
||||
},
|
||||
{
|
||||
id: 'oa-form-sink',
|
||||
|
||||
@@ -1,35 +1,49 @@
|
||||
<template>
|
||||
<view class="page">
|
||||
<view class="search-wrap">
|
||||
<input
|
||||
class="search"
|
||||
v-model="searchKeyword"
|
||||
confirm-type="search"
|
||||
placeholder="请输入审批单名称"
|
||||
/>
|
||||
<view class="toolbar">
|
||||
<view class="search-box" :class="{ 'is-focus': searchFocused }">
|
||||
<view class="search-ico" aria-hidden="true" />
|
||||
<input
|
||||
class="search-input"
|
||||
:value="searchKeyword"
|
||||
confirm-type="search"
|
||||
placeholder="搜索审批单"
|
||||
placeholder-class="search-ph"
|
||||
@input="onSearchInput"
|
||||
@focus="searchFocused = true"
|
||||
@blur="searchFocused = false"
|
||||
/>
|
||||
<view
|
||||
v-if="searchKeyword"
|
||||
class="search-clear"
|
||||
@click="searchKeyword = ''"
|
||||
>
|
||||
<text class="search-clear__x">×</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-x class="tabs" :show-scrollbar="false">
|
||||
<view class="tabs-inner">
|
||||
<view
|
||||
class="tab"
|
||||
:class="{ active: activeGroupId === '' }"
|
||||
@click="activeGroupId = ''"
|
||||
>
|
||||
全部
|
||||
</view>
|
||||
<view
|
||||
v-for="g in groupOptions"
|
||||
:key="g.id"
|
||||
class="tab"
|
||||
:class="{ active: activeGroupId === g.id }"
|
||||
@click="activeGroupId = g.id"
|
||||
>
|
||||
{{ g.groupName }}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<scroll-view scroll-x class="tabs" :show-scrollbar="false">
|
||||
<view
|
||||
class="tab"
|
||||
:class="{ active: activeGroupId === '' }"
|
||||
@click="activeGroupId = ''"
|
||||
>
|
||||
全部
|
||||
</view>
|
||||
<view
|
||||
v-for="g in groupOptions"
|
||||
:key="g.id"
|
||||
class="tab"
|
||||
:class="{ active: activeGroupId === g.id }"
|
||||
@click="activeGroupId = g.id"
|
||||
>
|
||||
{{ g.groupName }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="hint">选择一个审批单即可发起</view>
|
||||
|
||||
<view v-if="loading" class="state state-loading">
|
||||
<view class="pulse-dots" aria-hidden="true">
|
||||
<view class="pulse-dot" />
|
||||
@@ -45,7 +59,6 @@
|
||||
<view v-for="sec in displayGroups" :key="sec.id" class="section">
|
||||
<view class="section-hd">
|
||||
<text class="section-name">{{ sec.name }}</text>
|
||||
<view class="section-dot" />
|
||||
<text class="section-meta">{{ sec.apps.length }} 个表单</text>
|
||||
</view>
|
||||
<view class="card-row">
|
||||
@@ -55,18 +68,8 @@
|
||||
class="oa-app-card"
|
||||
@click="onPick(row)"
|
||||
>
|
||||
<view class="oa-app-card__main">
|
||||
<OaAppIcon :icon-url="row.iconUrl" size="card" />
|
||||
<view class="oa-app-card__body">
|
||||
<text class="oa-app-card__name">{{ row.appName }}</text>
|
||||
<text v-if="row.description" class="oa-app-card__desc">{{
|
||||
row.description
|
||||
}}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="oa-app-card__ops">
|
||||
<view class="oa-app-card__btn" @click.stop="onPick(row)">发起</view>
|
||||
</view>
|
||||
<OaAppIcon :icon-url="row.iconUrl" size="xs" />
|
||||
<text class="oa-app-card__name">{{ row.appName }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -77,7 +80,7 @@
|
||||
<script>
|
||||
import OaAppIcon from '../../../components/OaAppIcon.vue'
|
||||
import { fetchOaGroupList, fetchOaTemplates } from '../../../utils/oa-api.js'
|
||||
import { getToken } from '../../../utils/auth.js'
|
||||
import { forceRelogin, getToken } from '../../../utils/auth.js'
|
||||
import { stashOaStartTitle } from '../../../utils/oa-h5.js'
|
||||
|
||||
export default {
|
||||
@@ -88,6 +91,7 @@ export default {
|
||||
templates: [],
|
||||
groupOptions: [],
|
||||
searchKeyword: '',
|
||||
searchFocused: false,
|
||||
activeGroupId: ''
|
||||
}
|
||||
},
|
||||
@@ -132,12 +136,21 @@ export default {
|
||||
},
|
||||
onShow() {
|
||||
if (!getToken()) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
forceRelogin('请先登录')
|
||||
return
|
||||
}
|
||||
this.load()
|
||||
},
|
||||
methods: {
|
||||
onSearchInput(e) {
|
||||
const v =
|
||||
e && e.detail && e.detail.value != null
|
||||
? String(e.detail.value)
|
||||
: e && e.target
|
||||
? String(e.target.value || '')
|
||||
: ''
|
||||
this.searchKeyword = v
|
||||
},
|
||||
async load() {
|
||||
this.loading = true
|
||||
try {
|
||||
@@ -175,55 +188,132 @@ export default {
|
||||
.page {
|
||||
min-height: 100vh;
|
||||
padding-bottom: 24rpx;
|
||||
background: #f5f8f7;
|
||||
background: #f5f6f8;
|
||||
}
|
||||
.search-wrap {
|
||||
padding: 20rpx 24rpx 8rpx;
|
||||
.toolbar {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
background: #fff;
|
||||
}
|
||||
.search {
|
||||
.search-box {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
margin: 20rpx 24rpx 0;
|
||||
height: 72rpx;
|
||||
padding: 0 24rpx;
|
||||
border-radius: 16rpx;
|
||||
background: #f5f8f7;
|
||||
border-radius: 20rpx;
|
||||
border: 1rpx solid #e8eaed;
|
||||
background: #fafafa;
|
||||
box-sizing: border-box;
|
||||
transition: border-color 0.15s, background 0.15s, box-shadow 0.15s;
|
||||
}
|
||||
.search-box.is-focus {
|
||||
border-color: #cbd5e1;
|
||||
background: #fff;
|
||||
box-shadow: 0 0 0 6rpx rgba(148, 163, 184, 0.18);
|
||||
}
|
||||
.search-ico {
|
||||
position: relative;
|
||||
width: 26rpx;
|
||||
height: 26rpx;
|
||||
border: 3rpx solid #94a3b8;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.search-ico::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 12rpx;
|
||||
height: 3rpx;
|
||||
right: -8rpx;
|
||||
bottom: -5rpx;
|
||||
background: #94a3b8;
|
||||
border-radius: 3rpx;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.search-input {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1f2937;
|
||||
min-width: 0;
|
||||
}
|
||||
.search-ph {
|
||||
color: #b0b7c3;
|
||||
}
|
||||
.search-clear {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
border-radius: 50%;
|
||||
background: #e5e7eb;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.search-clear__x {
|
||||
font-size: 28rpx;
|
||||
line-height: 1;
|
||||
color: #6b7280;
|
||||
margin-top: -2rpx;
|
||||
}
|
||||
.tabs {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
padding: 16rpx 16rpx 8rpx;
|
||||
background: #fff;
|
||||
margin-top: 8rpx;
|
||||
padding: 0 8rpx;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1rpx solid #f1f5f9;
|
||||
}
|
||||
.tabs-inner {
|
||||
display: inline-flex;
|
||||
align-items: stretch;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
.tab {
|
||||
display: inline-block;
|
||||
margin-right: 16rpx;
|
||||
padding: 12rpx 24rpx;
|
||||
border-radius: 999rpx;
|
||||
background: #f5f8f7;
|
||||
color: #6b7b74;
|
||||
font-size: 26rpx;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80rpx;
|
||||
padding: 0 28rpx;
|
||||
font-size: 28rpx;
|
||||
color: #64748b;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.tab.active {
|
||||
color: #fff;
|
||||
background: #16a34a;
|
||||
color: #166534;
|
||||
font-weight: 600;
|
||||
}
|
||||
.hint {
|
||||
padding: 16rpx 24rpx;
|
||||
color: #6b7b74;
|
||||
font-size: 24rpx;
|
||||
.tab.active::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 28rpx;
|
||||
right: 28rpx;
|
||||
bottom: 0;
|
||||
height: 4rpx;
|
||||
border-radius: 4rpx 4rpx 0 0;
|
||||
background: #22c55e;
|
||||
}
|
||||
.state {
|
||||
padding: 80rpx 24rpx;
|
||||
text-align: center;
|
||||
color: #6b7b74;
|
||||
color: #999;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
/* 对齐 mobile-oa-h5 OaPulseLoading:品牌绿三点跳动 + muted 文案 */
|
||||
.state-loading {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 28rpx;
|
||||
padding: 120rpx 24rpx 80rpx;
|
||||
min-height: 50vh;
|
||||
padding: 96rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.pulse-dots {
|
||||
display: flex;
|
||||
@@ -234,8 +324,8 @@ export default {
|
||||
width: 16rpx;
|
||||
height: 16rpx;
|
||||
border-radius: 50%;
|
||||
background: #16a34a;
|
||||
animation: oa-start-bounce 1.2s ease-in-out infinite;
|
||||
background: var(--brand-green, #22c55e);
|
||||
animation: oa-pulse-bounce 1.2s ease-in-out infinite;
|
||||
}
|
||||
.pulse-dot:nth-child(2) {
|
||||
animation-delay: 0.15s;
|
||||
@@ -247,10 +337,11 @@ export default {
|
||||
}
|
||||
.state-loading__text {
|
||||
font-size: 26rpx;
|
||||
color: #6b7b74;
|
||||
font-weight: 500;
|
||||
color: var(--text-muted, #5f7a6d);
|
||||
line-height: 1.4;
|
||||
}
|
||||
@keyframes oa-start-bounce {
|
||||
@keyframes oa-pulse-bounce {
|
||||
0%,
|
||||
80%,
|
||||
100% {
|
||||
@@ -261,30 +352,32 @@ export default {
|
||||
}
|
||||
}
|
||||
.body {
|
||||
padding: 0 24rpx;
|
||||
padding: 24rpx 24rpx 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24rpx;
|
||||
}
|
||||
.section {
|
||||
margin-bottom: 28rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid #e5e7eb;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
padding: 8rpx 8rpx 12rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.section-hd {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
margin-bottom: 16rpx;
|
||||
min-height: 40rpx;
|
||||
min-height: 64rpx;
|
||||
padding: 0 16rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
.section-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
.section-dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
border-radius: 50%;
|
||||
background: #f87171;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.section-meta {
|
||||
margin-left: auto;
|
||||
font-size: 22rpx;
|
||||
@@ -293,67 +386,30 @@ export default {
|
||||
.card-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
.oa-app-card {
|
||||
width: calc((100% - 16rpx) / 2);
|
||||
width: 25%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
min-height: 180rpx;
|
||||
padding: 20rpx 20rpx 14rpx;
|
||||
border-radius: 20rpx;
|
||||
background: #fff;
|
||||
border: 1px solid #d8e3de;
|
||||
box-shadow: 0 4rpx 16rpx rgba(15, 23, 42, 0.06);
|
||||
align-items: center;
|
||||
gap: 12rpx;
|
||||
padding: 16rpx 8rpx 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.oa-app-card:active {
|
||||
opacity: 0.94;
|
||||
}
|
||||
.oa-app-card__main {
|
||||
display: flex;
|
||||
gap: 20rpx;
|
||||
align-items: flex-start;
|
||||
min-width: 0;
|
||||
}
|
||||
.oa-app-card__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6rpx;
|
||||
justify-content: center;
|
||||
opacity: 0.72;
|
||||
}
|
||||
.oa-app-card__name {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
width: 100%;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #334155;
|
||||
line-height: 1.35;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
.oa-app-card__desc {
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
text-align: center;
|
||||
word-break: break-all;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.oa-app-card__ops {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: 12rpx;
|
||||
border-top: 1px solid #f1f5f9;
|
||||
}
|
||||
.oa-app-card__btn {
|
||||
height: 44rpx;
|
||||
line-height: 44rpx;
|
||||
padding: 0 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #16a34a;
|
||||
color: #fff;
|
||||
font-size: 22rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
buildOaStartUrl,
|
||||
takeOaStartTitle
|
||||
} from '../../../utils/oa-h5.js'
|
||||
import { getToken } from '../../../utils/auth.js'
|
||||
import { forceRelogin, getToken } from '../../../utils/auth.js'
|
||||
import { fetchOaTemplateDetail } from '../../../utils/oa-api.js'
|
||||
|
||||
const CLOSE_ACTIONS = new Set([
|
||||
@@ -24,8 +24,7 @@ export default {
|
||||
},
|
||||
onLoad(query) {
|
||||
if (!getToken()) {
|
||||
uni.showToast({ title: '请先登录', icon: 'none' })
|
||||
setTimeout(() => uni.navigateBack(), 500)
|
||||
forceRelogin('请先登录')
|
||||
return
|
||||
}
|
||||
const appId = String((query && query.appId) || '')
|
||||
@@ -41,7 +40,7 @@ export default {
|
||||
|
||||
const src = buildOaStartUrl({ appId, draftId })
|
||||
if (!/\btoken=/.test(src)) {
|
||||
uni.showToast({ title: '未登录,无法打开 OA 表单', icon: 'none' })
|
||||
forceRelogin('请先登录')
|
||||
return
|
||||
}
|
||||
// 对齐水槽:onLoad 内同步赋值,与 pages/oa/form-sink/index 一致
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#16a34a" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="#22c55e" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="22 12 16 12 14 15 10 15 8 12 2 12"/>
|
||||
<path d="M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 341 B |
|
After Width: | Height: | Size: 3.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.6 KiB |
@@ -16,10 +16,7 @@ view, text {
|
||||
|
||||
.page-shell {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(840rpx 440rpx at 100% 0%, rgba(214, 163, 67, 0.14), transparent 55%),
|
||||
radial-gradient(760rpx 400rpx at 0% 20%, rgba(22, 163, 74, 0.1), transparent 50%),
|
||||
linear-gradient(180deg, #e8f5ef 0%, #f4f7f6 40%, #f7f9f8 100%);
|
||||
background: var(--bg-page);
|
||||
}
|
||||
|
||||
.safe-bottom {
|
||||
|
||||
@@ -16,14 +16,14 @@
|
||||
}
|
||||
|
||||
.d-attach-upload:active {
|
||||
border-color: #16a34a;
|
||||
border-color: #22c55e;
|
||||
}
|
||||
|
||||
.d-attach-upload .up-icon {
|
||||
width: 88rpx;
|
||||
height: 88rpx;
|
||||
border-radius: 24rpx;
|
||||
background: linear-gradient(145deg, #4ade80, #16a34a);
|
||||
background: linear-gradient(145deg, #4ade80, #22c55e);
|
||||
color: #fff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -41,13 +41,13 @@
|
||||
.d-attach-upload .up-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 700;
|
||||
color: #15803d;
|
||||
color: #22c55e;
|
||||
}
|
||||
|
||||
.d-attach-upload .up-sub {
|
||||
margin-top: 4rpx;
|
||||
font-size: 22rpx;
|
||||
color: #6b7b74;
|
||||
color: #5f7a6d;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
@@ -71,17 +71,17 @@
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 6rpx;
|
||||
background: var(--file-accent, #16a34a);
|
||||
background: var(--file-accent, #22c55e);
|
||||
}
|
||||
|
||||
.d-attach-card.ft-pdf {
|
||||
--file-accent: #d14343;
|
||||
--file-accent: #ef4444;
|
||||
}
|
||||
.d-attach-card.ft-img {
|
||||
--file-accent: #16a34a;
|
||||
--file-accent: #22c55e;
|
||||
}
|
||||
.d-attach-card.ft-xls {
|
||||
--file-accent: #189b65;
|
||||
--file-accent: #10b981;
|
||||
}
|
||||
.d-attach-card.ft-doc {
|
||||
--file-accent: #3b82f6;
|
||||
@@ -105,13 +105,13 @@
|
||||
}
|
||||
|
||||
.d-attach-card.ft-pdf .file-icon {
|
||||
background: linear-gradient(145deg, #f07171, #d14343);
|
||||
background: linear-gradient(145deg, #f07171, #ef4444);
|
||||
}
|
||||
.d-attach-card.ft-img .file-icon {
|
||||
background: linear-gradient(145deg, #4ade80, #16a34a);
|
||||
background: linear-gradient(145deg, #4ade80, #22c55e);
|
||||
}
|
||||
.d-attach-card.ft-xls .file-icon {
|
||||
background: linear-gradient(145deg, #4fbf8a, #189b65);
|
||||
background: linear-gradient(145deg, #4fbf8a, #10b981);
|
||||
}
|
||||
.d-attach-card.ft-doc .file-icon {
|
||||
background: linear-gradient(145deg, #6ea8ff, #3b82f6);
|
||||
@@ -169,8 +169,8 @@
|
||||
}
|
||||
|
||||
.d-attach-card .file-ops .btn-preview {
|
||||
background: var(--brand-green-soft, #f0fdf4);
|
||||
color: var(--brand-green-dark, #15803d);
|
||||
background: var(--brand-green-soft, #ecfdf5);
|
||||
color: var(--brand-green-dark, #16a34a);
|
||||
}
|
||||
|
||||
.d-attach-card .file-ops .btn-download {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
margin: 0 0 16rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: 650;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
}
|
||||
|
||||
.confirm-dialog__link {
|
||||
color: var(--brand-green, #16a34a);
|
||||
color: var(--brand-green, #22c55e);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@
|
||||
}
|
||||
|
||||
.confirm-dialog__btn--confirm {
|
||||
color: var(--brand-green, #16a34a);
|
||||
color: var(--brand-green, #22c55e);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
|
||||
.detail-page {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(760rpx 400rpx at 100% 8%, rgba(214, 163, 67, 0.14), transparent 55%),
|
||||
linear-gradient(180deg, #e8f5ef 0%, #f4f7f6 42%, #f7f9f8 100%);
|
||||
background: var(--bg-page, #ffffff);
|
||||
padding-bottom: 176rpx;
|
||||
}
|
||||
|
||||
@@ -17,17 +15,20 @@
|
||||
}
|
||||
|
||||
.detail-hero.status-warning {
|
||||
background: linear-gradient(155deg, #a67b20 0%, #d6a343 45%, #e8c06a 100%);
|
||||
background: linear-gradient(160deg, #fffbeb 0%, #fde68a 55%, #fbbf24 100%);
|
||||
color: #92400e;
|
||||
}
|
||||
.detail-hero.status-primary {
|
||||
@include hero-green;
|
||||
border-radius: 0 0 56rpx 56rpx;
|
||||
}
|
||||
.detail-hero.status-danger {
|
||||
background: linear-gradient(155deg, #9c2f2f 0%, #d14343 50%, #e36a6a 100%);
|
||||
background: linear-gradient(160deg, #fef2f2 0%, #fecaca 55%, #f87171 100%);
|
||||
color: #991b1b;
|
||||
}
|
||||
.detail-hero.status-success {
|
||||
background: linear-gradient(155deg, #0f6b48 0%, #189b65 52%, #3bb883 100%);
|
||||
background: linear-gradient(160deg, #ecfdf5 0%, #a7f3d0 55%, #6ee7b7 100%);
|
||||
color: #065f46;
|
||||
}
|
||||
|
||||
.detail-hero-body {
|
||||
@@ -52,13 +53,20 @@
|
||||
}
|
||||
|
||||
.detail-hero .type-chip {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: #fff;
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.detail-hero.status-primary .type-chip,
|
||||
.detail-hero.status-success .type-chip,
|
||||
.detail-hero.status-warning .type-chip,
|
||||
.detail-hero.status-danger .type-chip {
|
||||
background: rgba(15, 41, 30, 0.08);
|
||||
}
|
||||
|
||||
.detail-hero .status-chip {
|
||||
background: #fff;
|
||||
color: #173128;
|
||||
color: #0f291e;
|
||||
}
|
||||
|
||||
.detail-hero .hero-title {
|
||||
@@ -146,12 +154,12 @@
|
||||
width: 8rpx;
|
||||
height: 28rpx;
|
||||
border-radius: 999rpx;
|
||||
background: linear-gradient(180deg, #22c55e, #15803d);
|
||||
background: linear-gradient(180deg, #4ade80, #22c55e);
|
||||
}
|
||||
|
||||
.detail-section .sec-hd .sec-more {
|
||||
font-size: 24rpx;
|
||||
color: var(--brand-green, #16a34a);
|
||||
color: var(--brand-green, #22c55e);
|
||||
}
|
||||
|
||||
.kv-list {
|
||||
@@ -232,7 +240,7 @@
|
||||
gap: 20rpx;
|
||||
padding: 24rpx 28rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-top: 1rpx solid var(--border, #dcebe4);
|
||||
border-top: 1rpx solid var(--border, #d8f3e5);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,14 +49,14 @@
|
||||
}
|
||||
|
||||
.flow-item.done .flow-dot {
|
||||
background: linear-gradient(145deg, #4ade80, #16a34a);
|
||||
background: linear-gradient(145deg, #4ade80, #22c55e);
|
||||
}
|
||||
.flow-item.warn .flow-dot {
|
||||
background: linear-gradient(145deg, #ecc06a, #d6a343);
|
||||
box-shadow: 0 12rpx 28rpx rgba(214, 163, 67, 0.28);
|
||||
}
|
||||
.flow-item.danger .flow-dot {
|
||||
background: linear-gradient(145deg, #ef7a7a, #d14343);
|
||||
background: linear-gradient(145deg, #ef7a7a, #ef4444);
|
||||
box-shadow: 0 12rpx 28rpx rgba(209, 67, 67, 0.25);
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
|
||||
.flow-item.done .flow-badge {
|
||||
background: rgba(22, 163, 74, 0.12);
|
||||
color: #15803d;
|
||||
color: #22c55e;
|
||||
}
|
||||
.flow-item.warn .flow-badge {
|
||||
background: rgba(214, 163, 67, 0.16);
|
||||
@@ -134,7 +134,7 @@
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #6b7b74;
|
||||
color: #5f7a6d;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border: 1rpx solid #e8f0ec;
|
||||
border-radius: 999rpx;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
.form-page {
|
||||
min-height: 100vh;
|
||||
background: var(--bg-page, #f5f8f7);
|
||||
background: var(--bg-page, #ffffff);
|
||||
padding-bottom: 176rpx;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
gap: 20rpx;
|
||||
padding: 24rpx 28rpx calc(24rpx + env(safe-area-inset-bottom));
|
||||
background: rgba(255, 255, 255, 0.96);
|
||||
border-top: 1rpx solid var(--border, #dcebe4);
|
||||
border-top: 1rpx solid var(--border, #d8f3e5);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
@@ -53,15 +53,19 @@
|
||||
|
||||
.home-header .brand {
|
||||
font-size: 24rpx;
|
||||
opacity: 0.88;
|
||||
opacity: 0.78;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
|
||||
.home-logout {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
padding: 8rpx 4rpx;
|
||||
color: var(--header-fg, #065f46);
|
||||
opacity: 0.85;
|
||||
padding: 8rpx 20rpx;
|
||||
flex-shrink: 0;
|
||||
border: 1rpx solid rgba(6, 95, 70, 0.35);
|
||||
border-radius: 12rpx;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.home-header .hello {
|
||||
@@ -76,7 +80,7 @@
|
||||
justify-content: space-between;
|
||||
gap: 16rpx;
|
||||
font-size: 24rpx;
|
||||
opacity: 0.92;
|
||||
opacity: 0.82;
|
||||
}
|
||||
|
||||
.home-header .meta__left {
|
||||
@@ -90,7 +94,8 @@
|
||||
width: 8rpx;
|
||||
height: 8rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
background: currentColor;
|
||||
opacity: 0.45;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
@@ -115,21 +120,21 @@
|
||||
.panel-hd .panel-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 650;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
}
|
||||
|
||||
.panel-hd .hint {
|
||||
font-size: 22rpx;
|
||||
color: var(--text-muted, #6b7b74);
|
||||
color: var(--text-muted, #5f7a6d);
|
||||
}
|
||||
|
||||
.perm-banner {
|
||||
padding: 20rpx 24rpx;
|
||||
border-radius: 24rpx;
|
||||
background: var(--barley-yellow-soft, #fff7e8);
|
||||
border: 1rpx solid #f0e0b8;
|
||||
background: var(--barley-yellow-soft, #fffbeb);
|
||||
border: 1rpx solid #fde68a;
|
||||
font-size: 24rpx;
|
||||
color: #8a6520;
|
||||
color: #a16207;
|
||||
margin-bottom: 24rpx;
|
||||
line-height: 1.45;
|
||||
}
|
||||
@@ -15,7 +15,7 @@
|
||||
.home-banner__item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #f0fdf4;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.home-banner__video,
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
right: -100rpx;
|
||||
bottom: -140rpx;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
background: rgba(6, 95, 70, 0.06);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin: 8rpx 4rpx 28rpx;
|
||||
color: #fff;
|
||||
color: var(--header-fg, #065f46);
|
||||
}
|
||||
|
||||
.list-hero-copy .eyebrow {
|
||||
@@ -48,7 +48,7 @@
|
||||
.list-hero-copy .subline .em {
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
background: rgba(6, 95, 70, 0.1);
|
||||
border-radius: 999rpx;
|
||||
padding: 2rpx 16rpx;
|
||||
}
|
||||
@@ -81,7 +81,7 @@
|
||||
.seg-tab {
|
||||
border-radius: 22rpx;
|
||||
padding: 16rpx 8rpx;
|
||||
color: #6b7b74;
|
||||
color: #5f7a6d;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
@@ -98,13 +98,13 @@
|
||||
|
||||
.seg-tab.active {
|
||||
background: #fff;
|
||||
color: var(--brand-green-dark, #15803d);
|
||||
color: var(--brand-green-dark, #16a34a);
|
||||
font-weight: 650;
|
||||
box-shadow: 0 8rpx 24rpx rgba(21, 128, 61, 0.14);
|
||||
}
|
||||
|
||||
.seg-tab.active .count {
|
||||
color: var(--brand-green, #16a34a);
|
||||
color: var(--brand-green, #22c55e);
|
||||
}
|
||||
|
||||
.list-wrap {
|
||||
@@ -163,13 +163,13 @@
|
||||
color: #7a5610;
|
||||
}
|
||||
.biz-card.status-primary .type-icon {
|
||||
background: linear-gradient(145deg, #86efac, #16a34a);
|
||||
background: linear-gradient(145deg, #86efac, #22c55e);
|
||||
}
|
||||
.biz-card.status-danger .type-icon {
|
||||
background: linear-gradient(145deg, #ffb4b4, #e85a5a);
|
||||
}
|
||||
.biz-card.status-success .type-icon {
|
||||
background: linear-gradient(145deg, #b8efd4, #189b65);
|
||||
background: linear-gradient(145deg, #b8efd4, #10b981);
|
||||
}
|
||||
|
||||
.biz-card .title-wrap {
|
||||
@@ -193,7 +193,7 @@
|
||||
}
|
||||
.biz-card.status-primary .type-chip {
|
||||
background: rgba(22, 163, 74, 0.12);
|
||||
color: #15803d;
|
||||
color: #22c55e;
|
||||
}
|
||||
.biz-card.status-danger .type-chip {
|
||||
background: rgba(209, 67, 67, 0.12);
|
||||
@@ -225,11 +225,11 @@
|
||||
box-shadow: 0 4rpx 16rpx rgba(214, 163, 67, 0.22);
|
||||
}
|
||||
.biz-card.status-primary .status-pill {
|
||||
background: #16a34a;
|
||||
background: #22c55e;
|
||||
color: #fff;
|
||||
}
|
||||
.biz-card.status-danger .status-pill {
|
||||
background: #d14343;
|
||||
background: #ef4444;
|
||||
color: #fff;
|
||||
}
|
||||
.biz-card.status-success .status-pill {
|
||||
@@ -286,7 +286,7 @@
|
||||
color: #9a7424;
|
||||
}
|
||||
.biz-card.status-primary .side-metric .amt {
|
||||
color: #15803d;
|
||||
color: #22c55e;
|
||||
}
|
||||
.biz-card.status-danger .side-metric .amt {
|
||||
color: #b13535;
|
||||
@@ -351,13 +351,13 @@
|
||||
color: #a67b20;
|
||||
}
|
||||
.biz-card.status-primary .footer .left-hint {
|
||||
color: #16a34a;
|
||||
color: #22c55e;
|
||||
}
|
||||
.biz-card.status-danger .footer .left-hint {
|
||||
color: #d14343;
|
||||
color: #ef4444;
|
||||
}
|
||||
.biz-card.status-success .footer .left-hint {
|
||||
color: #189b65;
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.biz-card .footer .ops {
|
||||
|
||||
@@ -11,14 +11,14 @@
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 16rpx;
|
||||
gap: 8rpx;
|
||||
min-height: 168rpx;
|
||||
padding: 20rpx 8rpx 12rpx;
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
|
||||
.menu-card:active {
|
||||
background: var(--brand-green-soft, #f0fdf4);
|
||||
background: var(--brand-green-soft, #ecfdf5);
|
||||
}
|
||||
|
||||
.menu-icon {
|
||||
@@ -31,7 +31,7 @@
|
||||
background: #eef2f1;
|
||||
color: #3d524a;
|
||||
font-size: 48rpx;
|
||||
box-shadow: inset 0 0 0 1rpx rgba(31, 42, 38, 0.06);
|
||||
box-shadow: inset 0 0 0 1rpx rgba(15, 41, 30, 0.06);
|
||||
}
|
||||
|
||||
.menu-icon .v-icon--img {
|
||||
@@ -39,6 +39,19 @@
|
||||
height: 52rpx;
|
||||
}
|
||||
|
||||
/* 无色底:仅展示位图本身(如发起审批纸飞机),收紧容器避免与标题脱节 */
|
||||
.menu-icon.menu-icon--plain {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
background: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.menu-icon.menu-icon--plain .v-icon--img {
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
}
|
||||
|
||||
.menu-icon.tone-approve {
|
||||
background: #ffe4e6;
|
||||
color: #e11d48;
|
||||
@@ -46,8 +59,8 @@
|
||||
}
|
||||
|
||||
.menu-icon.tone-yellow {
|
||||
background: var(--barley-yellow-soft, #fff7e8);
|
||||
color: #a67b20;
|
||||
background: var(--barley-yellow-soft, #fffbeb);
|
||||
color: #a16207;
|
||||
}
|
||||
|
||||
.menu-icon.tone-slate {
|
||||
@@ -58,7 +71,7 @@
|
||||
.menu-title {
|
||||
font-size: 24rpx;
|
||||
line-height: 1.25;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
text-align: center;
|
||||
max-width: 144rpx;
|
||||
word-break: break-all;
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
z-index: 10;
|
||||
background: #fff;
|
||||
padding-top: env(safe-area-inset-top);
|
||||
border-bottom: 1rpx solid var(--border, #dcebe4);
|
||||
border-bottom: 1rpx solid var(--border, #d8f3e5);
|
||||
}
|
||||
|
||||
.protocol-nav__bar {
|
||||
@@ -41,7 +41,7 @@
|
||||
.protocol-nav__back-icon {
|
||||
font-size: 56rpx;
|
||||
line-height: 1;
|
||||
color: #1f2a26;
|
||||
color: #0f291e;
|
||||
font-weight: 300;
|
||||
margin-top: -4rpx;
|
||||
}
|
||||
@@ -51,7 +51,7 @@
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1f2a26;
|
||||
color: #0f291e;
|
||||
}
|
||||
|
||||
.protocol-body {
|
||||
@@ -61,7 +61,7 @@
|
||||
.protocol-meta {
|
||||
margin-bottom: 28rpx;
|
||||
font-size: 24rpx;
|
||||
color: var(--text-muted, #6b7b74);
|
||||
color: var(--text-muted, #5f7a6d);
|
||||
}
|
||||
|
||||
.protocol-section {
|
||||
@@ -72,14 +72,14 @@
|
||||
margin-bottom: 16rpx;
|
||||
font-size: 30rpx;
|
||||
font-weight: 650;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.protocol-section__p {
|
||||
margin-bottom: 12rpx;
|
||||
font-size: 26rpx;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
line-height: 1.7;
|
||||
text-align: justify;
|
||||
}
|
||||
@@ -32,13 +32,13 @@
|
||||
}
|
||||
|
||||
.todo-stat.tone-warn::before {
|
||||
background: var(--warning, #cf8a19);
|
||||
background: var(--warning, #d97706);
|
||||
}
|
||||
.todo-stat.tone-pay::before {
|
||||
background: var(--barley-yellow, #d6a343);
|
||||
background: var(--barley-yellow, #fbbf24);
|
||||
}
|
||||
.todo-stat.tone-danger::before {
|
||||
background: var(--danger, #d14343);
|
||||
background: var(--danger, #ef4444);
|
||||
}
|
||||
|
||||
.todo-stat.tone-warn {
|
||||
@@ -56,7 +56,7 @@
|
||||
font-size: 44rpx;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
@@ -67,14 +67,14 @@
|
||||
color: #9a7424;
|
||||
}
|
||||
.todo-stat.tone-danger .num {
|
||||
color: var(--danger, #d14343);
|
||||
color: var(--danger, #ef4444);
|
||||
}
|
||||
|
||||
.todo-stat .label {
|
||||
display: block;
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: var(--text-muted, #6b7b74);
|
||||
color: var(--text-muted, #5f7a6d);
|
||||
}
|
||||
|
||||
.todo-stat .stat-icon {
|
||||
@@ -92,7 +92,7 @@
|
||||
|
||||
.todo-stat.tone-warn .stat-icon {
|
||||
background: rgba(207, 138, 25, 0.12);
|
||||
color: var(--warning, #cf8a19);
|
||||
color: var(--warning, #d97706);
|
||||
}
|
||||
.todo-stat.tone-pay .stat-icon {
|
||||
background: rgba(214, 163, 67, 0.16);
|
||||
@@ -100,7 +100,7 @@
|
||||
}
|
||||
.todo-stat.tone-danger .stat-icon {
|
||||
background: rgba(209, 67, 67, 0.12);
|
||||
color: var(--danger, #d14343);
|
||||
color: var(--danger, #ef4444);
|
||||
}
|
||||
|
||||
.todo-list {
|
||||
@@ -115,13 +115,13 @@
|
||||
gap: 20rpx;
|
||||
width: 100%;
|
||||
padding: 20rpx;
|
||||
border: 1rpx solid var(--border, #dcebe4);
|
||||
border: 1rpx solid var(--border, #d8f3e5);
|
||||
border-radius: 24rpx;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.todo-item:active {
|
||||
background: var(--brand-green-soft, #f0fdf4);
|
||||
background: var(--brand-green-soft, #ecfdf5);
|
||||
border-color: #b7e5d0;
|
||||
}
|
||||
|
||||
@@ -138,15 +138,15 @@
|
||||
|
||||
.todo-item .ti-icon.i-approve {
|
||||
background: #fff3e0;
|
||||
color: var(--warning, #cf8a19);
|
||||
color: var(--warning, #d97706);
|
||||
}
|
||||
.todo-item .ti-icon.i-pay {
|
||||
background: var(--barley-yellow-soft, #fff7e8);
|
||||
background: var(--barley-yellow-soft, #fffbeb);
|
||||
color: #a67b20;
|
||||
}
|
||||
.todo-item .ti-icon.i-alert {
|
||||
background: #fdecec;
|
||||
color: var(--danger, #d14343);
|
||||
color: var(--danger, #ef4444);
|
||||
}
|
||||
|
||||
.todo-item .ti-body {
|
||||
@@ -157,14 +157,14 @@
|
||||
.todo-item .ti-title {
|
||||
font-size: 26rpx;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary, #1f2a26);
|
||||
color: var(--text-primary, #0f291e);
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.todo-item .ti-desc {
|
||||
margin-top: 4rpx;
|
||||
font-size: 22rpx;
|
||||
color: var(--text-muted, #6b7b74);
|
||||
color: var(--text-muted, #5f7a6d);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@mixin hero-green {
|
||||
background: linear-gradient(155deg, #14532d 0%, #16a34a 48%, #22c55e 100%);
|
||||
color: #ffffff;
|
||||
background: linear-gradient(160deg, #f0fdf4 0%, #d1fae5 58%, #a7f3d0 100%);
|
||||
color: var(--header-fg, #065f46);
|
||||
border-radius: 0 0 56rpx 56rpx;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
@mixin surface-card {
|
||||
background: var(--surface, #ffffff);
|
||||
border: 1rpx solid var(--border, #dcebe4);
|
||||
border: 1rpx solid var(--border, #d8f3e5);
|
||||
border-radius: var(--radius-card, 28rpx);
|
||||
box-shadow: var(--shadow-soft);
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
// 唯一 Token 源 — 品牌绿(青葱),禁止 web-admin #10b981
|
||||
$brand-green: #16a34a;
|
||||
$brand-green-dark: #15803d;
|
||||
$brand-green-soft: #f0fdf4;
|
||||
$barley-yellow: #d6a343;
|
||||
$barley-yellow-soft: #fff7e8;
|
||||
$bg-page: #f5f8f7;
|
||||
// 唯一 Token 源 — 方案 A「薄荷微风」:明亮清爽,勿用深墨绿顶栏
|
||||
// 主色 #22c55e;成功语义可用 #10b981,但主按钮勿单独改成 web-admin 主色链
|
||||
$brand-green: #22c55e;
|
||||
$brand-green-dark: #16a34a;
|
||||
$brand-green-soft: #ecfdf5;
|
||||
$header-fg: #065f46;
|
||||
$barley-yellow: #fbbf24;
|
||||
$barley-yellow-soft: #fffbeb;
|
||||
$bg-page: #ffffff;
|
||||
$surface: #ffffff;
|
||||
$text-primary: #1f2a26;
|
||||
$text-muted: #6b7b74;
|
||||
$border: #dcebe4;
|
||||
$danger: #d14343;
|
||||
$warning: #cf8a19;
|
||||
$success: #189b65;
|
||||
$text-primary: #0f291e;
|
||||
$text-muted: #5f7a6d;
|
||||
$border: #d8f3e5;
|
||||
$danger: #ef4444;
|
||||
$warning: #d97706;
|
||||
$success: #10b981;
|
||||
$radius-card: 28rpx;
|
||||
$radius-lg: 40rpx;
|
||||
|
||||
@@ -19,6 +21,7 @@ $radius-lg: 40rpx;
|
||||
--brand-green: #{$brand-green};
|
||||
--brand-green-dark: #{$brand-green-dark};
|
||||
--brand-green-soft: #{$brand-green-soft};
|
||||
--header-fg: #{$header-fg};
|
||||
--barley-yellow: #{$barley-yellow};
|
||||
--barley-yellow-soft: #{$barley-yellow-soft};
|
||||
--bg-page: #{$bg-page};
|
||||
@@ -31,5 +34,5 @@ $radius-lg: 40rpx;
|
||||
--success: #{$success};
|
||||
--radius-card: #{$radius-card};
|
||||
--radius-lg: #{$radius-lg};
|
||||
--shadow-soft: 0 8rpx 32rpx rgba(21, 128, 61, 0.08);
|
||||
--shadow-soft: 0 8rpx 32rpx rgba(22, 163, 74, 0.07);
|
||||
}
|
||||
@@ -1,21 +1,48 @@
|
||||
/* Vant 4 CSS 变量 — 对齐品牌绿 Token,禁止 #10b981 */
|
||||
/* Vant 4 CSS 变量 — 方案 A 薄荷微风 */
|
||||
page {
|
||||
--van-primary-color: #16a34a;
|
||||
--van-success-color: #189b65;
|
||||
--van-danger-color: #d14343;
|
||||
--van-warning-color: #cf8a19;
|
||||
--van-primary-color: #22c55e;
|
||||
--van-success-color: #10b981;
|
||||
--van-danger-color: #ef4444;
|
||||
--van-warning-color: #d97706;
|
||||
|
||||
--van-button-primary-background: #16a34a;
|
||||
--van-button-primary-border-color: #16a34a;
|
||||
--van-button-primary-background: #22c55e;
|
||||
--van-button-primary-border-color: #22c55e;
|
||||
|
||||
--van-nav-bar-background: transparent;
|
||||
--van-nav-bar-title-text-color: #ffffff;
|
||||
--van-nav-bar-icon-color: #ffffff;
|
||||
--van-nav-bar-title-text-color: #065f46;
|
||||
--van-nav-bar-icon-color: #065f46;
|
||||
|
||||
--van-tabs-bottom-bar-color: #16a34a;
|
||||
--van-tag-primary-color: #16a34a;
|
||||
--van-steps-active-color: #16a34a;
|
||||
--van-tabs-bottom-bar-color: #22c55e;
|
||||
--van-tag-primary-color: #22c55e;
|
||||
--van-steps-active-color: #22c55e;
|
||||
|
||||
--van-notice-bar-background: #f0fdf4;
|
||||
--van-notice-bar-text-color: #15803d;
|
||||
--van-notice-bar-background: #ecfdf5;
|
||||
--van-notice-bar-text-color: #16a34a;
|
||||
|
||||
/* 表单 Cell / Field 更紧凑(选择框与输入行) */
|
||||
--van-cell-vertical-padding: 8px;
|
||||
--van-cell-horizontal-padding: 16px;
|
||||
--van-cell-font-size: 14px;
|
||||
--van-cell-line-height: 22px;
|
||||
--van-field-input-text-color: #0f291e;
|
||||
--van-field-placeholder-text-color: #5f7a6d;
|
||||
}
|
||||
|
||||
/* 选择类触发条(is-link / readonly):再压矮一点 */
|
||||
.van-field.van-cell--clickable,
|
||||
.van-field--readonly.van-cell--clickable {
|
||||
min-height: 40px;
|
||||
}
|
||||
|
||||
.van-field.van-cell--clickable .van-field__control,
|
||||
.van-field--readonly.van-cell--clickable .van-field__control {
|
||||
font-size: 13px;
|
||||
min-height: 22px;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.van-field.van-cell--clickable .van-cell__right-icon,
|
||||
.van-field--readonly.van-cell--clickable .van-cell__right-icon {
|
||||
font-size: 14px;
|
||||
line-height: 22px;
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
/** 本地登录会话(无网络依赖,供 request / auth 共用,避免循环引用) */
|
||||
|
||||
const TOKEN_KEY = 'zkxw_mobile_token'
|
||||
const REFRESH_TOKEN_KEY = 'zkxw_mobile_refresh_token'
|
||||
const USER_KEY = 'zkxw_mobile_user'
|
||||
|
||||
export function getToken() {
|
||||
try {
|
||||
return uni.getStorageSync(TOKEN_KEY) || ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function getRefreshToken() {
|
||||
try {
|
||||
return uni.getStorageSync(REFRESH_TOKEN_KEY) || ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function isLoggedIn() {
|
||||
return !!getToken()
|
||||
}
|
||||
|
||||
export function getUser() {
|
||||
try {
|
||||
const raw = uni.getStorageSync(USER_KEY)
|
||||
return raw ? JSON.parse(raw) : null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function clearSession() {
|
||||
try {
|
||||
uni.removeStorageSync(TOKEN_KEY)
|
||||
uni.removeStorageSync(REFRESH_TOKEN_KEY)
|
||||
uni.removeStorageSync(USER_KEY)
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
export function saveSession({ token, refreshToken, userInfo }) {
|
||||
uni.setStorageSync(TOKEN_KEY, token || '')
|
||||
uni.setStorageSync(REFRESH_TOKEN_KEY, refreshToken || '')
|
||||
uni.setStorageSync(USER_KEY, JSON.stringify(userInfo || null))
|
||||
}
|
||||
|
||||
/** 更新本地缓存的用户信息(保留 token)。 */
|
||||
export function updateStoredUser(userInfo) {
|
||||
try {
|
||||
uni.setStorageSync(USER_KEY, JSON.stringify(userInfo || null))
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
let _reloginPending = false
|
||||
|
||||
function isOnLoginPage() {
|
||||
try {
|
||||
const pages = getCurrentPages()
|
||||
const cur = pages && pages[pages.length - 1]
|
||||
const route = (cur && cur.route) || ''
|
||||
return String(route).indexOf('pages/login') !== -1
|
||||
} catch (e) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Token 失效:清本地会话并回到登录页(防抖,避免并发 401 连跳)。
|
||||
* @param {string} [message]
|
||||
*/
|
||||
export function forceRelogin(message) {
|
||||
if (_reloginPending) return
|
||||
clearSession()
|
||||
if (isOnLoginPage()) return
|
||||
_reloginPending = true
|
||||
const title =
|
||||
(typeof message === 'string' && message.trim()) ||
|
||||
'登录已失效,请重新登录'
|
||||
uni.showToast({ title, icon: 'none', duration: 2000 })
|
||||
setTimeout(() => {
|
||||
uni.reLaunch({
|
||||
url: '/pages/login/index',
|
||||
complete() {
|
||||
_reloginPending = false
|
||||
},
|
||||
fail() {
|
||||
_reloginPending = false
|
||||
}
|
||||
})
|
||||
}, 400)
|
||||
}
|
||||
@@ -1,65 +1,26 @@
|
||||
import { validateCredentials } from './validate-credentials.js'
|
||||
import { buildEncryptedLoginBody } from './login-rsa.js'
|
||||
import { get, post } from './request.js'
|
||||
import {
|
||||
getToken,
|
||||
getRefreshToken,
|
||||
isLoggedIn,
|
||||
getUser,
|
||||
clearSession,
|
||||
saveSession,
|
||||
updateStoredUser,
|
||||
forceRelogin
|
||||
} from './auth-session.js'
|
||||
|
||||
export { validateCredentials }
|
||||
|
||||
const TOKEN_KEY = 'zkxw_mobile_token'
|
||||
const REFRESH_TOKEN_KEY = 'zkxw_mobile_refresh_token'
|
||||
const USER_KEY = 'zkxw_mobile_user'
|
||||
|
||||
export function getToken() {
|
||||
try {
|
||||
return uni.getStorageSync(TOKEN_KEY) || ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function getRefreshToken() {
|
||||
try {
|
||||
return uni.getStorageSync(REFRESH_TOKEN_KEY) || ''
|
||||
} catch (e) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
export function isLoggedIn() {
|
||||
return !!getToken()
|
||||
}
|
||||
|
||||
export function getUser() {
|
||||
try {
|
||||
const raw = uni.getStorageSync(USER_KEY)
|
||||
return raw ? JSON.parse(raw) : null
|
||||
} catch (e) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
export function clearSession() {
|
||||
try {
|
||||
uni.removeStorageSync(TOKEN_KEY)
|
||||
uni.removeStorageSync(REFRESH_TOKEN_KEY)
|
||||
uni.removeStorageSync(USER_KEY)
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
function saveSession({ token, refreshToken, userInfo }) {
|
||||
uni.setStorageSync(TOKEN_KEY, token || '')
|
||||
uni.setStorageSync(REFRESH_TOKEN_KEY, refreshToken || '')
|
||||
uni.setStorageSync(USER_KEY, JSON.stringify(userInfo || null))
|
||||
}
|
||||
|
||||
/** 更新本地缓存的用户信息(保留 token)。 */
|
||||
export function updateStoredUser(userInfo) {
|
||||
try {
|
||||
uni.setStorageSync(USER_KEY, JSON.stringify(userInfo || null))
|
||||
} catch (e) {
|
||||
/* ignore */
|
||||
}
|
||||
export {
|
||||
getToken,
|
||||
getRefreshToken,
|
||||
isLoggedIn,
|
||||
getUser,
|
||||
clearSession,
|
||||
updateStoredUser,
|
||||
forceRelogin
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*
|
||||
* 微信公众平台须配置 request / 业务域名:www.chengyantek.cn(HTTPS,安全组放行 443)
|
||||
*/
|
||||
const USE_REMOTE_TEST = false
|
||||
const USE_REMOTE_TEST = true
|
||||
|
||||
/** 本机联调 */
|
||||
const LOCAL_API_BASE_URL = 'http://127.0.0.1:8000/api'
|
||||
|
||||
@@ -2,16 +2,36 @@
|
||||
* 主页宫格图标配色:每项可自定义 iconBg / iconFg。
|
||||
* 未配置时按 icon 名回退到此处默认色对。
|
||||
*/
|
||||
/** OA 宫格 PNG 自带颜色,不套色底 */
|
||||
const OA_PNG_ICONS = new Set([
|
||||
'approve',
|
||||
'oa-drafts',
|
||||
'oa-pending',
|
||||
'oa-done',
|
||||
'oa-mine',
|
||||
'oa-cc'
|
||||
])
|
||||
|
||||
export const MENU_ICON_COLOR_DEFAULTS = {
|
||||
approve: { iconBg: '#ffe4e6', iconFg: '#e11d48' },
|
||||
approve: { iconBg: 'transparent', iconFg: '' },
|
||||
'oa-drafts': { iconBg: 'transparent', iconFg: '' },
|
||||
'oa-pending': { iconBg: 'transparent', iconFg: '' },
|
||||
'oa-done': { iconBg: 'transparent', iconFg: '' },
|
||||
'oa-mine': { iconBg: 'transparent', iconFg: '' },
|
||||
'oa-cc': { iconBg: 'transparent', iconFg: '' },
|
||||
'orders-o': { iconBg: '#eef2f1', iconFg: '#3d524a' },
|
||||
'apps-o': { iconBg: '#eef2f1', iconFg: '#3d524a' }
|
||||
}
|
||||
|
||||
export function resolveMenuIconColors(menu) {
|
||||
const icon = String(menu?.icon || '').trim()
|
||||
// OA PNG 自带色:忽略接口下发的色底,避免灰底盖住图标
|
||||
if (OA_PNG_ICONS.has(icon)) {
|
||||
return { iconBg: 'transparent', iconFg: '' }
|
||||
}
|
||||
const fallback = MENU_ICON_COLOR_DEFAULTS[icon] || {}
|
||||
const iconBg = String(menu?.iconBg || menu?.icon_bg || fallback.iconBg || '').trim()
|
||||
const iconFg = String(menu?.iconFg || menu?.icon_fg || fallback.iconFg || '').trim()
|
||||
return { iconBg, iconFg }
|
||||
}
|
||||
|
||||
@@ -1,4 +1,42 @@
|
||||
import { API_BASE_URL, REQUEST_TIMEOUT_MS } from './config.js'
|
||||
import { forceRelogin } from './auth-session.js'
|
||||
|
||||
/** 这些接口的业务 code=401 表示凭据错误等,不是会话过期 */
|
||||
const AUTH_EXEMPT_PATHS = [
|
||||
'/web/auth/login',
|
||||
'/web/auth/login-rsa-public-key',
|
||||
'/web/auth/refresh-token',
|
||||
'/mini/auth/wechat-login'
|
||||
]
|
||||
|
||||
function isAuthExemptPath(path) {
|
||||
const p = String(path || '')
|
||||
for (let i = 0; i < AUTH_EXEMPT_PATHS.length; i++) {
|
||||
if (p.indexOf(AUTH_EXEMPT_PATHS[i]) !== -1) return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function hasBearerToken(header) {
|
||||
const auth = (header && header.Authorization) || ''
|
||||
return /^Bearer\s+\S+/i.test(String(auth))
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP 401,或已带 token 的业务 code=401 → 强制重新登录。
|
||||
* 登录接口豁免,避免「账号密码错误」被当成会话失效。
|
||||
*/
|
||||
function maybeForceRelogin(options, httpStatus, body) {
|
||||
if (isAuthExemptPath(options && options.url)) return
|
||||
const bizCode = body && body.code
|
||||
const msg =
|
||||
(body && typeof body.message === 'string' && body.message.trim()) ||
|
||||
(body && typeof body.detail === 'string' && body.detail.trim()) ||
|
||||
''
|
||||
if (httpStatus === 401 || (bizCode === 401 && hasBearerToken(options && options.header))) {
|
||||
forceRelogin(msg || '登录已失效,请重新登录')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析后端统一响应。成功返回 data;失败抛 Error(message 为业务文案)。
|
||||
@@ -41,6 +79,7 @@ export function request(options) {
|
||||
{ 'Content-Type': 'application/json' },
|
||||
options.header || {}
|
||||
)
|
||||
const reqOpts = { url: options.url, header }
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
uni.request({
|
||||
@@ -51,13 +90,16 @@ export function request(options) {
|
||||
timeout: REQUEST_TIMEOUT_MS,
|
||||
success(res) {
|
||||
try {
|
||||
const body = res.data
|
||||
maybeForceRelogin(reqOpts, res.statusCode, body)
|
||||
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
resolve(unwrapResponse(res.data, res.statusCode))
|
||||
resolve(unwrapResponse(body, res.statusCode))
|
||||
return
|
||||
}
|
||||
// HTTP 非 2xx:若 body 仍是业务包,优先展示 message
|
||||
try {
|
||||
resolve(unwrapResponse(res.data, res.statusCode))
|
||||
resolve(unwrapResponse(body, res.statusCode))
|
||||
} catch (bizErr) {
|
||||
reject(bizErr)
|
||||
}
|
||||
|
||||